From 8e84b5b0152cdfe5bacbf8649cae127c1094d925 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 24 May 2022 15:32:00 +0200 Subject: [PATCH 001/175] increase title part height when CC shows * add 4px extra space to title bar part when CC is enabled * trigger relayout when toggling CC so that new height is taken into account * macos: manually position traffic lights so that they line-up with CC --- .../platform/windows/electron-main/window.ts | 19 +++++++++++++++++++ src/vs/workbench/browser/layout.ts | 2 +- .../browser/parts/titlebar/titlebarPart.ts | 5 ++++- .../parts/titlebar/titlebarPart.ts | 8 +++++++- 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index 3b8ba525827..e181acbdfe0 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -271,6 +271,25 @@ export class CodeWindow extends Disposable implements ICodeWindow { this._id = this._win.id; + // re-position traffic light if command center is visible + if (useCustomTitleStyle && isMacintosh) { + const ccConfigKey = 'window.experimental.commandCenter'; + const trafficLightUpdater = () => { + const on = this.configurationService.getValue(ccConfigKey); + if (on) { + this._win.setTrafficLightPosition({ x: 7, y: 8 }); + } else { + this._win.setTrafficLightPosition({ x: 7, y: 6 }); + } + }; + trafficLightUpdater(); + this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(ccConfigKey)) { + trafficLightUpdater(); + } + }); + } + // Open devtools if instructed from command line args if (this.environmentMainService.args['open-devtools'] === true) { this._win.webContents.openDevTools(); diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 10a0a5e03ec..43168e2f927 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -269,7 +269,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } // Title Menu changes - this._register(this.titleService.onDidChangeTitleMenuVisibility(() => this._onDidLayout.fire(this._dimension))); + this._register(this.titleService.onDidChangeTitleMenuVisibility(() => this.layout())); // Theme changes this._register(this.themeService.onDidColorThemeChange(() => this.updateStyles())); diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index e549362ba2d..c1fbf2fd6bb 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -45,7 +45,10 @@ export class TitlebarPart extends Part implements ITitleService { readonly minimumWidth: number = 0; readonly maximumWidth: number = Number.POSITIVE_INFINITY; - get minimumHeight(): number { return 30 / (this.currentMenubarVisibility === 'hidden' || getZoomFactor() < 1 ? getZoomFactor() : 1); } + get minimumHeight(): number { + const ccPadding = this.isCommandCenterVisible ? 4 : 0; + return (30 + ccPadding) / (this.currentMenubarVisibility === 'hidden' || getZoomFactor() < 1 ? getZoomFactor() : 1); + } get maximumHeight(): number { return this.minimumHeight; } //#endregion diff --git a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts index 75a1a5fa6bd..f5962e8f33d 100644 --- a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts @@ -37,7 +37,13 @@ export class TitlebarPart extends BrowserTitleBarPart { return 22; } - override get minimumHeight(): number { return isMacintosh ? this.getMacTitlebarSize() / getZoomFactor() : super.minimumHeight; } + override get minimumHeight(): number { + if (!isMacintosh) { + return super.minimumHeight; + } + const ccPadding = this.isCommandCenterVisible ? 4 : 0; + return (this.getMacTitlebarSize() + ccPadding) / getZoomFactor(); + } override get maximumHeight(): number { return this.minimumHeight; } protected override readonly environmentService: INativeWorkbenchEnvironmentService; From 0075b0156cf38d471c84497679fb6815f3cb1192 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 24 May 2022 15:35:46 +0200 Subject: [PATCH 002/175] add missing CC rename --- src/vs/workbench/browser/layout.ts | 2 +- src/vs/workbench/browser/parts/titlebar/titlebarPart.ts | 6 +++--- src/vs/workbench/services/title/common/titleService.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 43168e2f927..9d7adb44001 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -269,7 +269,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } // Title Menu changes - this._register(this.titleService.onDidChangeTitleMenuVisibility(() => this.layout())); + this._register(this.titleService.onDidChangeCommandCenterVisibility(() => this.layout())); // Theme changes this._register(this.themeService.onDidColorThemeChange(() => this.updateStyles())); diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index c1fbf2fd6bb..b69113cd97e 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -56,8 +56,8 @@ export class TitlebarPart extends Part implements ITitleService { private _onMenubarVisibilityChange = this._register(new Emitter()); readonly onMenubarVisibilityChange = this._onMenubarVisibilityChange.event; - private readonly _onDidChangeTitleMenuVisibility = new Emitter(); - readonly onDidChangeTitleMenuVisibility: Event = this._onDidChangeTitleMenuVisibility.event; + private readonly _onDidChangeCommandCenterVisibility = new Emitter(); + readonly onDidChangeCommandCenterVisibility: Event = this._onDidChangeCommandCenterVisibility.event; protected rootContainer!: HTMLElement; protected windowControls: HTMLElement | undefined; @@ -143,7 +143,7 @@ export class TitlebarPart extends Part implements ITitleService { if (event.affectsConfiguration(TitlebarPart.configCommandCenter)) { this.updateTitle(); this.adjustTitleMarginToCenter(); - this._onDidChangeTitleMenuVisibility.fire(); + this._onDidChangeCommandCenterVisibility.fire(); } } diff --git a/src/vs/workbench/services/title/common/titleService.ts b/src/vs/workbench/services/title/common/titleService.ts index 92aca9f92cd..57b7ae7430e 100644 --- a/src/vs/workbench/services/title/common/titleService.ts +++ b/src/vs/workbench/services/title/common/titleService.ts @@ -31,7 +31,7 @@ export interface ITitleService { /** * An event when the title menu is enabled/disabled */ - readonly onDidChangeTitleMenuVisibility: Event; + readonly onDidChangeCommandCenterVisibility: Event; /** * Update some environmental title properties. From 67cc6771cc09766911ce6783045c3ee733a64feb Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 24 May 2022 15:50:34 +0200 Subject: [PATCH 003/175] center align menubar menu buttons, don't assign fixed height to titlebar container --- src/vs/base/browser/ui/menu/menubar.css | 1 + src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/menu/menubar.css b/src/vs/base/browser/ui/menu/menubar.css index b9dd13780e9..59b16df8d13 100644 --- a/src/vs/base/browser/ui/menu/menubar.css +++ b/src/vs/base/browser/ui/menu/menubar.css @@ -30,6 +30,7 @@ zoom: 1; white-space: nowrap; outline: 0; + align-self: center; } .menubar.compact { diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 77abc653614..a43cd721f2f 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -41,7 +41,7 @@ .monaco-workbench.web .part.titlebar>.titlebar-container, .monaco-workbench.windows .part.titlebar>.titlebar-container, .monaco-workbench.linux .part.titlebar>.titlebar-container { - height: 30px; + /* height: 30px; */ line-height: 22px; justify-content: left; } From 6c12a03285d987979e3fe95d7900e0f5878f286f Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 24 May 2022 16:05:38 +0200 Subject: [PATCH 004/175] make sure config listener is getting disposed --- src/vs/platform/windows/electron-main/window.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index e181acbdfe0..ced1af3fa1d 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -287,7 +287,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { if (e.affectsConfiguration(ccConfigKey)) { trafficLightUpdater(); } - }); + }, undefined, this._store); } // Open devtools if instructed from command line args From d3f47f47c643f491f4120b6623da509b325878c1 Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 25 May 2022 16:08:54 +0200 Subject: [PATCH 005/175] titlebar with CC has same height as tabs bar --- src/vs/platform/windows/electron-main/window.ts | 2 +- src/vs/workbench/browser/parts/titlebar/titlebarPart.ts | 2 +- .../workbench/electron-sandbox/parts/titlebar/titlebarPart.ts | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index ced1af3fa1d..5b8b66794e6 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -277,7 +277,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { const trafficLightUpdater = () => { const on = this.configurationService.getValue(ccConfigKey); if (on) { - this._win.setTrafficLightPosition({ x: 7, y: 8 }); + this._win.setTrafficLightPosition({ x: 7, y: 9 }); } else { this._win.setTrafficLightPosition({ x: 7, y: 6 }); } diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index b69113cd97e..b966ddd27ce 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -46,7 +46,7 @@ export class TitlebarPart extends Part implements ITitleService { readonly minimumWidth: number = 0; readonly maximumWidth: number = Number.POSITIVE_INFINITY; get minimumHeight(): number { - const ccPadding = this.isCommandCenterVisible ? 4 : 0; + const ccPadding = this.isCommandCenterVisible ? 5 : 0; return (30 + ccPadding) / (this.currentMenubarVisibility === 'hidden' || getZoomFactor() < 1 ? getZoomFactor() : 1); } get maximumHeight(): number { return this.minimumHeight; } diff --git a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts index f5962e8f33d..6f1b202254d 100644 --- a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts @@ -41,8 +41,7 @@ export class TitlebarPart extends BrowserTitleBarPart { if (!isMacintosh) { return super.minimumHeight; } - const ccPadding = this.isCommandCenterVisible ? 4 : 0; - return (this.getMacTitlebarSize() + ccPadding) / getZoomFactor(); + return (this.isCommandCenterVisible ? 35 : this.getMacTitlebarSize()) / getZoomFactor(); } override get maximumHeight(): number { return this.minimumHeight; } From fec3576f94298810a5201586feac6f87a8e99eb5 Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 10 Jun 2022 14:32:13 +0200 Subject: [PATCH 006/175] update to new config name --- src/vs/platform/windows/electron-main/window.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index a3d0cec1318..0cfcdff594d 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -273,7 +273,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { // re-position traffic light if command center is visible if (useCustomTitleStyle && isMacintosh) { - const ccConfigKey = 'window.experimental.commandCenter'; + const ccConfigKey = 'window.commandCenter'; const trafficLightUpdater = () => { const on = this.configurationService.getValue(ccConfigKey); if (on) { From 75251b84ed4639e013cc7d5b677bbeda18645392 Mon Sep 17 00:00:00 2001 From: Johannes Date: Sat, 11 Jun 2022 19:03:50 +0200 Subject: [PATCH 007/175] explore allowing snippet text edits as workspace edit --- .../browser/services/bulkEditService.ts | 4 +- .../snippet/browser/snippetController2.ts | 86 ++++++++++++++++++- .../contrib/snippet/browser/snippetSession.ts | 2 +- .../contrib/bulkEdit/browser/bulkTextEdits.ts | 27 ++++-- 4 files changed, 107 insertions(+), 12 deletions(-) diff --git a/src/vs/editor/browser/services/bulkEditService.ts b/src/vs/editor/browser/services/bulkEditService.ts index 558376fae33..1fa168aedb1 100644 --- a/src/vs/editor/browser/services/bulkEditService.ts +++ b/src/vs/editor/browser/services/bulkEditService.ts @@ -45,9 +45,9 @@ export class ResourceEdit { export class ResourceTextEdit extends ResourceEdit { constructor( readonly resource: URI, - readonly textEdit: TextEdit, + readonly textEdit: TextEdit & { insertAsSnippet?: boolean }, readonly versionId?: number, - metadata?: WorkspaceEditMetadata + metadata?: WorkspaceEditMetadata, ) { super(metadata); } diff --git a/src/vs/editor/contrib/snippet/browser/snippetController2.ts b/src/vs/editor/contrib/snippet/browser/snippetController2.ts index bb8282d9ee5..77c8e579f5c 100644 --- a/src/vs/editor/contrib/snippet/browser/snippetController2.ts +++ b/src/vs/editor/contrib/snippet/browser/snippetController2.ts @@ -6,7 +6,8 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { EditorCommand, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { EditorAction2, EditorCommand, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { IBulkEditService, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { ISelection } from 'vs/editor/common/core/selection'; @@ -16,11 +17,13 @@ import { CompletionItem, CompletionItemKind, CompletionItemProvider } from 'vs/e import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; import { ITextModel } from 'vs/editor/common/model'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; -import { Choice } from 'vs/editor/contrib/snippet/browser/snippetParser'; +import { Choice, SnippetParser } from 'vs/editor/contrib/snippet/browser/snippetParser'; import { showSimpleSuggestions } from 'vs/editor/contrib/suggest/browser/suggest'; import { OvertypingCapturer } from 'vs/editor/contrib/suggest/browser/suggestOvertypingCapturer'; import { localize } from 'vs/nls'; +import { registerAction2 } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ILogService } from 'vs/platform/log/common/log'; import { SnippetSession } from './snippetSession'; @@ -346,3 +349,82 @@ export function performSnippetEdit(editor: ICodeEditor, snippet: string, selecti controller.insert(snippet); return controller.isInSnippet(); } + + +export type ISnippetEdit = { + range: Range; + snippet: string; +}; + +// --- + +export function performSnippetEdits(editor: ICodeEditor, edits: ISnippetEdit[]) { + + if (!editor.hasModel()) { + return false; + } + if (edits.length === 0) { + return false; + } + + const model = editor.getModel(); + let newText = ''; + let last: ISnippetEdit | undefined; + edits.sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range)); + + for (const item of edits) { + if (last) { + const between = Range.fromPositions(last.range.getEndPosition(), item.range.getStartPosition()); + const text = model.getValueInRange(between); + newText += SnippetParser.escape(text); + } + newText += item.snippet; + last = item; + } + + const controller = SnippetController2.get(editor); + if (!controller) { + return false; + } + model.pushStackElement(); + const range = Range.plusRange(edits[0].range, edits[edits.length - 1].range); + editor.setSelection(range); + controller.insert(newText, { undoStopBefore: false }); + return controller.isInSnippet(); +} + + +registerAction2(class extends EditorAction2 { + + constructor() { + super({ + id: 'snippet', + title: 'Extract constant snippet', + f1: true + }); + } + + async runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: any[]) { + if (!editor.hasModel()) { + return; + } + const bulkEdit = accessor.get(IBulkEditService); + await bulkEdit.apply([ + new ResourceTextEdit(editor.getModel().uri, { + range: new Range(1, 1, 1, 1), + text: `const \${1:foo} = 123;`, + insertAsSnippet: true + }), + new ResourceTextEdit(editor.getModel().uri, { + range: new Range(3, 4, 3, 7), + text: `\${1:foo}`, + insertAsSnippet: true + }), + new ResourceTextEdit(editor.getModel().uri, { + range: new Range(3, 8, 3, 8), + text: `\$0`, + insertAsSnippet: true + }), + ]); + } +}); diff --git a/src/vs/editor/contrib/snippet/browser/snippetSession.ts b/src/vs/editor/contrib/snippet/browser/snippetSession.ts index d1629256724..3ad72329e91 100644 --- a/src/vs/editor/contrib/snippet/browser/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/browser/snippetSession.ts @@ -49,7 +49,7 @@ export class OneSnippet { this._placeholderGroupsIdx = -1; } - public initialize(textChange: TextChange): void { + initialize(textChange: TextChange): void { this._offset = textChange.newPosition; } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts index b4cf1ff3071..e05bcaee18d 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts @@ -19,6 +19,8 @@ import { ResourceMap } from 'vs/base/common/map'; import { IModelService } from 'vs/editor/common/services/model'; import { ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { SnippetParser } from 'vs/editor/contrib/snippet/browser/snippetParser'; +import { performSnippetEdits } from 'vs/editor/contrib/snippet/browser/snippetController2'; type ValidationResult = { canApply: true } | { canApply: false; reason: URI }; @@ -27,7 +29,7 @@ class ModelEditTask implements IDisposable { readonly model: ITextModel; private _expectedModelVersionId: number | undefined; - protected _edits: ISingleEditOperation[]; + protected _edits: (ISingleEditOperation & { insertAsSnippet?: boolean })[]; protected _newEol: EndOfLineSequence | undefined; constructor(private readonly _modelReference: IReference) { @@ -75,7 +77,7 @@ class ModelEditTask implements IDisposable { } else { range = Range.lift(textEdit.range); } - this._edits.push(EditOperation.replaceMove(range, textEdit.text)); + this._edits.push({ ...EditOperation.replaceMove(range, textEdit.text), insertAsSnippet: textEdit.insertAsSnippet }); } validate(): ValidationResult { @@ -91,7 +93,9 @@ class ModelEditTask implements IDisposable { apply(): void { if (this._edits.length > 0) { - this._edits = this._edits.sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range)); + this._edits = this._edits + .sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range)) + .map(edit => ({ ...edit, text: edit.text && SnippetParser.escape(edit.text) })); this.model.pushEditOperations(null, this._edits, () => null); } if (this._newEol !== undefined) { @@ -121,10 +125,19 @@ class EditorEditTask extends ModelEditTask { super.apply(); return; } - if (this._edits.length > 0) { - this._edits = this._edits.sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range)); - this._editor.executeEdits('', this._edits); + + const insertAsSnippet = this._edits.every(edit => edit.insertAsSnippet); + if (insertAsSnippet) { + // todo@jrieken what ABOUT EOL? + performSnippetEdits(this._editor, this._edits.map(edit => ({ range: Range.lift(edit.range!), snippet: edit.text! }))); + + } else { + this._edits = this._edits + .sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range)) + .map(edit => ({ ...edit, text: edit.text && SnippetParser.escape(edit.text) })); + this._editor.executeEdits('', this._edits); + } } if (this._newEol !== undefined) { if (this._editor.hasModel()) { @@ -193,7 +206,7 @@ export class BulkTextEdits { let makeMinimal = false; if (this._editor?.getModel()?.uri.toString() === ref.object.textEditorModel.uri.toString()) { task = new EditorEditTask(ref, this._editor); - makeMinimal = true; + makeMinimal = true && false; } else { task = new ModelEditTask(ref); } From 03768103983d6358634f120c727b74cd84ed6bfc Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 13 Jun 2022 11:33:42 +0200 Subject: [PATCH 008/175] add API proposal for `TextEdit#newText: SnippetString` --- src/vs/workbench/api/common/extHost.api.impl.ts | 2 +- src/vs/workbench/api/common/extHost.protocol.ts | 2 +- src/vs/workbench/api/common/extHostBulkEdits.ts | 7 +++++-- .../api/common/extHostTypeConverters.ts | 4 ++-- src/vs/workbench/api/common/extHostTypes.ts | 1 + .../api/test/browser/extHostBulkEdits.test.ts | 5 +++-- .../extensions/common/extensionsApiProposals.ts | 1 + .../vscode.proposed.snippetWorkspaceEdit.d.ts | 16 ++++++++++++++++ 8 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 src/vscode-dts/vscode.proposed.snippetWorkspaceEdit.d.ts diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index cd752d09994..e7ccb442179 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -856,7 +856,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostWorkspace.saveAll(includeUntitled); }, applyEdit(edit: vscode.WorkspaceEdit): Thenable { - return extHostBulkEdits.applyWorkspaceEdit(edit); + return extHostBulkEdits.applyWorkspaceEdit(edit, extension); }, createFileSystemWatcher: (pattern, ignoreCreate, ignoreChange, ignoreDelete): vscode.FileSystemWatcher => { return extHostFileSystemEvent.createFileSystemWatcher(extHostWorkspace, extension, pattern, ignoreCreate, ignoreChange, ignoreDelete); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index d156a5d2a03..bfd174d9f1c 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1606,7 +1606,7 @@ export interface IWorkspaceFileEditDto { export interface IWorkspaceTextEditDto { _type: WorkspaceEditType.Text; resource: UriComponents; - edit: languages.TextEdit; + edit: languages.TextEdit & { insertAsSnippet?: boolean }; modelVersionId?: number; metadata?: IWorkspaceEditEntryMetadataDto; } diff --git a/src/vs/workbench/api/common/extHostBulkEdits.ts b/src/vs/workbench/api/common/extHostBulkEdits.ts index 6547c861438..2126d41724e 100644 --- a/src/vs/workbench/api/common/extHostBulkEdits.ts +++ b/src/vs/workbench/api/common/extHostBulkEdits.ts @@ -3,10 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { MainContext, MainThreadBulkEditsShape } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { WorkspaceEdit } from 'vs/workbench/api/common/extHostTypeConverters'; +import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import type * as vscode from 'vscode'; export class ExtHostBulkEdits { @@ -26,8 +28,9 @@ export class ExtHostBulkEdits { }; } - applyWorkspaceEdit(edit: vscode.WorkspaceEdit): Promise { - const dto = WorkspaceEdit.from(edit, this._versionInformationProvider); + applyWorkspaceEdit(edit: vscode.WorkspaceEdit, extension: IExtensionDescription): Promise { + const allowSnippetTextEdit = isProposedApiEnabled(extension, 'snippetWorkspaceEdit'); + const dto = WorkspaceEdit.from(edit, this._versionInformationProvider, allowSnippetTextEdit); return this._proxy.$tryApplyWorkspaceEdit(dto); } } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 5bf0397e70b..b8ce4b3a300 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -569,7 +569,7 @@ export namespace WorkspaceEdit { getNotebookDocumentVersion(uri: URI): number | undefined; } - export function from(value: vscode.WorkspaceEdit, versionInfo?: IVersionInformationProvider): extHostProtocol.IWorkspaceEditDto { + export function from(value: vscode.WorkspaceEdit, versionInfo?: IVersionInformationProvider, allowSnippetTextEdit?: boolean): extHostProtocol.IWorkspaceEditDto { const result: extHostProtocol.IWorkspaceEditDto = { edits: [] }; @@ -602,7 +602,7 @@ export namespace WorkspaceEdit { result.edits.push({ _type: extHostProtocol.WorkspaceEditType.Text, resource: entry.uri, - edit: TextEdit.from(entry.edit), + edit: { ...TextEdit.from(entry.edit), insertAsSnippet: allowSnippetTextEdit && entry.edit.newText2 instanceof types.SnippetString }, modelVersionId: !toCreate.has(entry.uri) ? versionInfo?.getTextDocumentVersion(entry.uri) : undefined, metadata: entry.metadata }); diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 69bd5b1e18a..ed7be92f183 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -549,6 +549,7 @@ export class TextEdit { protected _range: Range; protected _newText: string | null; + newText2?: string | SnippetString; protected _newEol?: EndOfLine; get range(): Range { diff --git a/src/vs/workbench/api/test/browser/extHostBulkEdits.test.ts b/src/vs/workbench/api/test/browser/extHostBulkEdits.test.ts index 4974c24314c..3b393b35347 100644 --- a/src/vs/workbench/api/test/browser/extHostBulkEdits.test.ts +++ b/src/vs/workbench/api/test/browser/extHostBulkEdits.test.ts @@ -12,6 +12,7 @@ import { SingleProxyRPCProtocol, TestRPCProtocol } from 'vs/workbench/api/test/c import { NullLogService } from 'vs/platform/log/common/log'; import { assertType } from 'vs/base/common/types'; import { ExtHostBulkEdits } from 'vs/workbench/api/common/extHostBulkEdits'; +import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; suite('ExtHostBulkEdits.applyWorkspaceEdit', () => { @@ -46,7 +47,7 @@ suite('ExtHostBulkEdits.applyWorkspaceEdit', () => { test('uses version id if document available', async () => { const edit = new extHostTypes.WorkspaceEdit(); edit.replace(resource, new extHostTypes.Range(0, 0, 0, 0), 'hello'); - await bulkEdits.applyWorkspaceEdit(edit); + await bulkEdits.applyWorkspaceEdit(edit, nullExtensionDescription); assert.strictEqual(workspaceResourceEdits.edits.length, 1); const [first] = workspaceResourceEdits.edits; assertType(first._type === WorkspaceEditType.Text); @@ -56,7 +57,7 @@ suite('ExtHostBulkEdits.applyWorkspaceEdit', () => { test('does not use version id if document is not available', async () => { const edit = new extHostTypes.WorkspaceEdit(); edit.replace(URI.parse('foo:bar2'), new extHostTypes.Range(0, 0, 0, 0), 'hello'); - await bulkEdits.applyWorkspaceEdit(edit); + await bulkEdits.applyWorkspaceEdit(edit, nullExtensionDescription); assert.strictEqual(workspaceResourceEdits.edits.length, 1); const [first] = workspaceResourceEdits.edits; assertType(first._type === WorkspaceEditType.Text); diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 5280e6584a2..0b2ce2139e9 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -51,6 +51,7 @@ export const allApiProposals = Object.freeze({ scmInput: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmInput.d.ts', scmSelectedProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmSelectedProvider.d.ts', scmValidation: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmValidation.d.ts', + snippetWorkspaceEdit: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.snippetWorkspaceEdit.d.ts', taskPresentationGroup: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.taskPresentationGroup.d.ts', telemetry: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.telemetry.d.ts', terminalDataWriteEvent: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalDataWriteEvent.d.ts', diff --git a/src/vscode-dts/vscode.proposed.snippetWorkspaceEdit.d.ts b/src/vscode-dts/vscode.proposed.snippetWorkspaceEdit.d.ts new file mode 100644 index 00000000000..43305c7b56e --- /dev/null +++ b/src/vscode-dts/vscode.proposed.snippetWorkspaceEdit.d.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/145374 + + export interface TextEdit { + + // will be merged with newText + // will NOT be supported everywhere, only: `workspace.applyEdit` + newText2?: string | SnippetString; + } +} From c7f15a57231ff901a0dda7d3630e1341b00cea41 Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 13 Jun 2022 14:13:17 +0200 Subject: [PATCH 009/175] remove sample command --- .../snippet/browser/snippetController2.ts | 41 +------------------ 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/src/vs/editor/contrib/snippet/browser/snippetController2.ts b/src/vs/editor/contrib/snippet/browser/snippetController2.ts index 77c8e579f5c..43e72f6ce57 100644 --- a/src/vs/editor/contrib/snippet/browser/snippetController2.ts +++ b/src/vs/editor/contrib/snippet/browser/snippetController2.ts @@ -6,8 +6,7 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { EditorAction2, EditorCommand, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { IBulkEditService, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; +import { EditorCommand, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { ISelection } from 'vs/editor/common/core/selection'; @@ -21,9 +20,7 @@ import { Choice, SnippetParser } from 'vs/editor/contrib/snippet/browser/snippet import { showSimpleSuggestions } from 'vs/editor/contrib/suggest/browser/suggest'; import { OvertypingCapturer } from 'vs/editor/contrib/suggest/browser/suggestOvertypingCapturer'; import { localize } from 'vs/nls'; -import { registerAction2 } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ILogService } from 'vs/platform/log/common/log'; import { SnippetSession } from './snippetSession'; @@ -392,39 +389,3 @@ export function performSnippetEdits(editor: ICodeEditor, edits: ISnippetEdit[]) controller.insert(newText, { undoStopBefore: false }); return controller.isInSnippet(); } - - -registerAction2(class extends EditorAction2 { - - constructor() { - super({ - id: 'snippet', - title: 'Extract constant snippet', - f1: true - }); - } - - async runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: any[]) { - if (!editor.hasModel()) { - return; - } - const bulkEdit = accessor.get(IBulkEditService); - await bulkEdit.apply([ - new ResourceTextEdit(editor.getModel().uri, { - range: new Range(1, 1, 1, 1), - text: `const \${1:foo} = 123;`, - insertAsSnippet: true - }), - new ResourceTextEdit(editor.getModel().uri, { - range: new Range(3, 4, 3, 7), - text: `\${1:foo}`, - insertAsSnippet: true - }), - new ResourceTextEdit(editor.getModel().uri, { - range: new Range(3, 8, 3, 8), - text: `\$0`, - insertAsSnippet: true - }), - ]); - } -}); From 29567d11ab100d1f953a3a5b33fca2e9f8500952 Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 13 Jun 2022 14:32:41 +0200 Subject: [PATCH 010/175] fix issue with serialization, add todo for hacks --- .../workbench/api/common/extHostTypeConverters.ts | 13 ++++++++++--- .../contrib/bulkEdit/browser/bulkTextEdits.ts | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index b8ce4b3a300..ebe234c5b33 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -598,14 +598,21 @@ export namespace WorkspaceEdit { }); } else if (entry._type === types.FileEditType.Text) { + // text edits - result.edits.push({ + const dto = { _type: extHostProtocol.WorkspaceEditType.Text, resource: entry.uri, - edit: { ...TextEdit.from(entry.edit), insertAsSnippet: allowSnippetTextEdit && entry.edit.newText2 instanceof types.SnippetString }, + edit: TextEdit.from(entry.edit), modelVersionId: !toCreate.has(entry.uri) ? versionInfo?.getTextDocumentVersion(entry.uri) : undefined, metadata: entry.metadata - }); + }; + if (allowSnippetTextEdit && entry.edit.newText2 instanceof types.SnippetString) { + dto.edit.insertAsSnippet = true; + dto.edit.text = entry.edit.newText2.value; + } + result.edits.push(dto); + } else if (entry._type === types.FileEditType.Cell) { result.edits.push({ _type: extHostProtocol.WorkspaceEditType.Cell, diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts index e05bcaee18d..047600a3912 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts @@ -206,7 +206,7 @@ export class BulkTextEdits { let makeMinimal = false; if (this._editor?.getModel()?.uri.toString() === ref.object.textEditorModel.uri.toString()) { task = new EditorEditTask(ref, this._editor); - makeMinimal = true && false; + makeMinimal = true && false; // todo@jrieken HACK } else { task = new ModelEditTask(ref); } From e0d3a8e2a8435a3f92ef7112559de7ae70245ef7 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 14 Jun 2022 13:55:51 +0200 Subject: [PATCH 011/175] storage - first cut profile support --- src/vs/code/electron-main/app.ts | 4 +- .../notification/common/notification.ts | 11 +- .../storage/browser/storageService.ts | 77 ++++++--- src/vs/platform/storage/common/storage.ts | 81 ++++++++-- .../test/browser/storageService.test.ts | 16 +- .../test/common/storageService.test.ts | 44 ++++-- .../electron-main/storageMainService.test.ts | 86 +++++++++-- .../platform/windows/electron-main/window.ts | 6 +- .../workspacesHistoryMainService.ts | 18 +-- src/vs/workbench/browser/web.main.ts | 6 +- src/vs/workbench/common/memento.ts | 76 ++++----- .../common/notificationService.ts | 19 ++- src/vs/workbench/test/common/memento.test.ts | 146 ++++++++++-------- 13 files changed, 395 insertions(+), 195 deletions(-) diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 218d0c30314..8fb281645b9 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -70,7 +70,7 @@ import { SharedProcess } from 'vs/platform/sharedProcess/electron-main/sharedPro import { ISignService } from 'vs/platform/sign/common/sign'; import { IStateMainService } from 'vs/platform/state/electron-main/state'; import { StorageDatabaseChannel } from 'vs/platform/storage/electron-main/storageIpc'; -import { GlobalStorageMainService, IGlobalStorageMainService, IStorageMainService, StorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; +import { ApplicationStorageMainService, IApplicationStorageMainService, IStorageMainService, StorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; import { resolveCommonProperties } from 'vs/platform/telemetry/common/commonProperties'; import { ITelemetryService, machineIdKey, TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; import { TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc'; @@ -648,7 +648,7 @@ export class CodeApplication extends Disposable { // Storage services.set(IStorageMainService, new SyncDescriptor(StorageMainService)); - services.set(IGlobalStorageMainService, new SyncDescriptor(GlobalStorageMainService)); + services.set(IApplicationStorageMainService, new SyncDescriptor(ApplicationStorageMainService)); // External terminal if (isWindows) { diff --git a/src/vs/platform/notification/common/notification.ts b/src/vs/platform/notification/common/notification.ts index 99e8716173c..5c4fa815ab7 100644 --- a/src/vs/platform/notification/common/notification.ts +++ b/src/vs/platform/notification/common/notification.ts @@ -45,9 +45,16 @@ export enum NeverShowAgainScope { WORKSPACE, /** - * Will never show this notification on any workspace again. + * Will never show this notification on any workspace of the same + * profile again. */ - GLOBAL + GLOBAL, + + /** + * Will never show this notification on any workspace across all + * profiles again. + */ + APPLICATION } export interface INeverShowAgainOptions { diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts index 645049cb7c2..f8ed2d1c5c8 100644 --- a/src/vs/platform/storage/browser/storageService.ts +++ b/src/vs/platform/storage/browser/storageService.ts @@ -9,42 +9,64 @@ import { Promises } from 'vs/base/common/async'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { hash } from 'vs/base/common/hash'; +import { isEqual } from 'vs/base/common/resources'; import { InMemoryStorageDatabase, isStorageItemsChangeEvent, IStorage, IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest, Storage } from 'vs/base/parts/storage/common/storage'; import { ILogService } from 'vs/platform/log/common/log'; import { AbstractStorageService, IS_NEW_KEY, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IAnyWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export class BrowserStorageService extends AbstractStorageService { private static BROWSER_DEFAULT_FLUSH_INTERVAL = 5 * 1000; // every 5s because async operations are not permitted on shutdown + private applicationStorage: IStorage | undefined; private globalStorage: IStorage | undefined; private workspaceStorage: IStorage | undefined; + private applicationStorageDatabase: IIndexedDBStorageDatabase | undefined; private globalStorageDatabase: IIndexedDBStorageDatabase | undefined; private workspaceStorageDatabase: IIndexedDBStorageDatabase | undefined; get hasPendingUpdate(): boolean { - return Boolean(this.globalStorageDatabase?.hasPendingUpdate || this.workspaceStorageDatabase?.hasPendingUpdate); + return Boolean( + this.applicationStorageDatabase?.hasPendingUpdate || + this.globalStorageDatabase?.hasPendingUpdate || + this.workspaceStorageDatabase?.hasPendingUpdate + ); } constructor( private readonly payload: IAnyWorkspaceIdentifier, - @ILogService private readonly logService: ILogService + @ILogService private readonly logService: ILogService, + @IUserDataProfilesService private readonly userDataProfileService: IUserDataProfilesService ) { super({ flushInterval: BrowserStorageService.BROWSER_DEFAULT_FLUSH_INTERVAL }); } private getId(scope: StorageScope): string { - return scope === StorageScope.GLOBAL ? 'global' : this.payload.id; + switch (scope) { + case StorageScope.APPLICATION: + return 'global'; // use the default profile global DB for application scope + case StorageScope.GLOBAL: + if (isEqual(this.userDataProfileService.currentProfile.location, this.userDataProfileService.defaultProfile.location)) { + return 'global'; // default profile DB has a fixed name for backwards compatibility + } else { + return `global-${hash(this.userDataProfileService.currentProfile.location.toString()).toString(16)}`; + } + case StorageScope.WORKSPACE: + return this.payload.id; + } } protected async doInitialize(): Promise { // Create Storage in Parallel - const [workspaceStorageDatabase, globalStorageDatabase] = await Promises.settled([ - IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.WORKSPACE) }, this.logService), - IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.GLOBAL), broadcastChanges: true /* only for global storage */ }, this.logService) + const [applicationStorageDatabase, globalStorageDatabase, workspaceStorageDatabase] = await Promises.settled([ + IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.APPLICATION), broadcastChanges: true }, this.logService), + IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.GLOBAL), broadcastChanges: true }, this.logService), + IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.WORKSPACE) }, this.logService) ]); // Workspace Storage @@ -52,6 +74,11 @@ export class BrowserStorageService extends AbstractStorageService { this.workspaceStorage = this._register(new Storage(this.workspaceStorageDatabase)); this._register(this.workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key))); + // Application Storage + this.applicationStorageDatabase = this._register(applicationStorageDatabase); + this.applicationStorage = this._register(new Storage(this.applicationStorageDatabase)); + this._register(this.applicationStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.APPLICATION, key))); + // Global Storage this.globalStorageDatabase = this._register(globalStorageDatabase); this.globalStorage = this._register(new Storage(this.globalStorageDatabase)); @@ -60,28 +87,30 @@ export class BrowserStorageService extends AbstractStorageService { // Init both await Promises.settled([ this.workspaceStorage.init(), - this.globalStorage.init() + this.globalStorage.init(), + this.applicationStorage.init() ]); - // Check to see if this is the first time we are "opening" the application - const firstOpen = this.globalStorage.getBoolean(IS_NEW_KEY); - if (firstOpen === undefined) { - this.globalStorage.set(IS_NEW_KEY, true); - } else if (firstOpen) { - this.globalStorage.set(IS_NEW_KEY, false); - } - - // Check to see if this is the first time we are "opening" this workspace - const firstWorkspaceOpen = this.workspaceStorage.getBoolean(IS_NEW_KEY); - if (firstWorkspaceOpen === undefined) { - this.workspaceStorage.set(IS_NEW_KEY, true); - } else if (firstWorkspaceOpen) { - this.workspaceStorage.set(IS_NEW_KEY, false); + // Apply is-new markers + for (const storage of [this.applicationStorage, this.globalStorage, this.workspaceStorage]) { + const firstOpen = storage.getBoolean(IS_NEW_KEY); + if (firstOpen === undefined) { + storage.set(IS_NEW_KEY, true); + } else if (firstOpen) { + storage.set(IS_NEW_KEY, false); + } } } protected getStorage(scope: StorageScope): IStorage | undefined { - return scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage; + switch (scope) { + case StorageScope.APPLICATION: + return this.applicationStorage; + case StorageScope.GLOBAL: + return this.globalStorage; + default: + return this.workspaceStorage; + } } protected getLogDetails(scope: StorageScope): string | undefined { @@ -115,6 +144,7 @@ export class BrowserStorageService extends AbstractStorageService { // On all other browsers, we keep the databases opened because // we expect data to be written when the unload happens. if (isSafari) { + this.applicationStorage?.close(); this.globalStorageDatabase?.close(); this.workspaceStorageDatabase?.close(); } @@ -127,7 +157,7 @@ export class BrowserStorageService extends AbstractStorageService { async clear(): Promise { // Clear key/values - for (const scope of [StorageScope.GLOBAL, StorageScope.WORKSPACE]) { + for (const scope of [StorageScope.APPLICATION, StorageScope.GLOBAL, StorageScope.WORKSPACE]) { for (const target of [StorageTarget.USER, StorageTarget.MACHINE]) { for (const key of this.keys(scope, target)) { this.remove(key, scope); @@ -139,6 +169,7 @@ export class BrowserStorageService extends AbstractStorageService { // Clear databases await Promises.settled([ + this.applicationStorageDatabase?.clear() ?? Promise.resolve(), this.globalStorageDatabase?.clear() ?? Promise.resolve(), this.workspaceStorageDatabase?.clear() ?? Promise.resolve() ]); diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts index 7d4ce4f359a..ececf161093 100644 --- a/src/vs/platform/storage/common/storage.ts +++ b/src/vs/platform/storage/common/storage.ts @@ -68,7 +68,7 @@ export interface IStorageService { * the provided `defaultValue` if the element is `null` or `undefined`. * * @param scope allows to define the scope of the storage operation - * to either the current workspace only or all workspaces. + * to either the current workspace only, all workspaces or all profiles. */ get(key: string, scope: StorageScope, fallbackValue: string): string; get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined; @@ -79,7 +79,7 @@ export interface IStorageService { * The element will be converted to a `boolean`. * * @param scope allows to define the scope of the storage operation - * to either the current workspace only or all workspaces. + * to either the current workspace only, all workspaces or all profiles. */ getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean; getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined; @@ -91,7 +91,7 @@ export interface IStorageService { * base of `10`. * * @param scope allows to define the scope of the storage operation - * to either the current workspace only or all workspaces. + * to either the current workspace only, all workspaces or all profiles. */ getNumber(key: string, scope: StorageScope, fallbackValue: number): number; getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined; @@ -102,7 +102,7 @@ export interface IStorageService { * remove the entry under the key. * * @param scope allows to define the scope of the storage operation - * to either the current workspace only or all workspaces. + * to either the current workspace only, all workspaces or all profiles. * * @param target allows to define the target of the storage operation * to either the current machine or user. @@ -113,7 +113,8 @@ export interface IStorageService { * Delete an element stored under the provided key from storage. * * The scope argument allows to define the scope of the storage - * operation to either the current workspace only or all workspaces. + * operation to either the current workspace only, all workspaces + * or all profiles. */ remove(key: string, scope: StorageScope): void; @@ -126,7 +127,7 @@ export interface IStorageService { * will be excluded from the results. * * @param scope allows to define the scope for the keys - * to either the current workspace only or all workspaces. + * to either the current workspace only, all workspaces or all profiles. * * @param target allows to define the target for the keys * to either the current machine or user. @@ -163,14 +164,19 @@ export interface IStorageService { export const enum StorageScope { /** - * The stored data will be scoped to all workspaces. + * The stored data will be scoped to all workspaces across all profiles. */ - GLOBAL, + APPLICATION = -1, + + /** + * The stored data will be scoped to all workspaces of the same profile. + */ + GLOBAL = 0, /** * The stored data will be scoped to the current workspace. */ - WORKSPACE + WORKSPACE = 1 } export const enum StorageTarget { @@ -302,10 +308,16 @@ export abstract class AbstractStorageService extends Disposable implements IStor if (key === TARGET_KEY) { // Clear our cached version which is now out of date - if (scope === StorageScope.GLOBAL) { - this._globalKeyTargets = undefined; - } else if (scope === StorageScope.WORKSPACE) { - this._workspaceKeyTargets = undefined; + switch (scope) { + case StorageScope.APPLICATION: + this._applicationKeyTargets = undefined; + break; + case StorageScope.GLOBAL: + this._globalKeyTargets = undefined; + break; + case StorageScope.WORKSPACE: + this._workspaceKeyTargets = undefined; + break; } // Emit as `didChangeTarget` event @@ -440,8 +452,24 @@ export abstract class AbstractStorageService extends Disposable implements IStor return this._globalKeyTargets; } + private _applicationKeyTargets: IKeyTargets | undefined = undefined; + private get applicationKeyTargets(): IKeyTargets { + if (!this._applicationKeyTargets) { + this._applicationKeyTargets = this.loadKeyTargets(StorageScope.APPLICATION); + } + + return this._applicationKeyTargets; + } + private getKeyTargets(scope: StorageScope): IKeyTargets { - return scope === StorageScope.GLOBAL ? this.globalKeyTargets : this.workspaceKeyTargets; + switch (scope) { + case StorageScope.APPLICATION: + return this.applicationKeyTargets; + case StorageScope.GLOBAL: + return this.globalKeyTargets; + default: + return this.workspaceKeyTargets; + } } private loadKeyTargets(scope: StorageScope): { [key: string]: StorageTarget } { @@ -466,6 +494,7 @@ export abstract class AbstractStorageService extends Disposable implements IStor // Signal event to collect changes this._onWillSaveState.fire({ reason }); + const applicationStorage = this.getStorage(StorageScope.APPLICATION); const globalStorage = this.getStorage(StorageScope.GLOBAL); const workspaceStorage = this.getStorage(StorageScope.WORKSPACE); @@ -474,6 +503,7 @@ export abstract class AbstractStorageService extends Disposable implements IStor // Unspecific reason: just wait when data is flushed case WillSaveStateReason.NONE: await Promises.settled([ + applicationStorage?.whenFlushed() ?? Promise.resolve(), globalStorage?.whenFlushed() ?? Promise.resolve(), workspaceStorage?.whenFlushed() ?? Promise.resolve() ]); @@ -483,6 +513,7 @@ export abstract class AbstractStorageService extends Disposable implements IStor // and not hit any delays that might be there case WillSaveStateReason.SHUTDOWN: await Promises.settled([ + applicationStorage?.flush(0) ?? Promise.resolve(), globalStorage?.flush(0) ?? Promise.resolve(), workspaceStorage?.flush(0) ?? Promise.resolve() ]); @@ -515,22 +546,38 @@ export abstract class AbstractStorageService extends Disposable implements IStor export class InMemoryStorageService extends AbstractStorageService { + private readonly applicationStorage = this._register(new Storage(new InMemoryStorageDatabase())); private readonly globalStorage = this._register(new Storage(new InMemoryStorageDatabase())); private readonly workspaceStorage = this._register(new Storage(new InMemoryStorageDatabase())); constructor() { super(); - this._register(this.workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key))); + this._register(this.applicationStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.APPLICATION, key))); this._register(this.globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); + this._register(this.workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key))); } protected getStorage(scope: StorageScope): IStorage { - return scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage; + switch (scope) { + case StorageScope.APPLICATION: + return this.applicationStorage; + case StorageScope.GLOBAL: + return this.globalStorage; + default: + return this.workspaceStorage; + } } protected getLogDetails(scope: StorageScope): string | undefined { - return scope === StorageScope.GLOBAL ? 'inMemory (global)' : 'inMemory (workspace)'; + switch (scope) { + case StorageScope.APPLICATION: + return 'inMemory (application)'; + case StorageScope.GLOBAL: + return 'inMemory (global)'; + default: + return 'inMemory (workspace)'; + } } protected async doInitialize(): Promise { } diff --git a/src/vs/platform/storage/test/browser/storageService.test.ts b/src/vs/platform/storage/test/browser/storageService.test.ts index cf51c013ef8..56c76e70f35 100644 --- a/src/vs/platform/storage/test/browser/storageService.test.ts +++ b/src/vs/platform/storage/test/browser/storageService.test.ts @@ -6,15 +6,19 @@ import { strictEqual } from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; import { Storage } from 'vs/base/parts/storage/common/storage'; +import { mock } from 'vs/base/test/common/mock'; import { flakySuite } from 'vs/base/test/common/testUtils'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { FileService } from 'vs/platform/files/common/fileService'; import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; import { NullLogService } from 'vs/platform/log/common/log'; import { BrowserStorageService, IndexedDBStorageDatabase } from 'vs/platform/storage/browser/storageService'; import { StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { createSuite } from 'vs/platform/storage/test/common/storageService.test'; +import { UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; async function createStorageService(): Promise<[DisposableStore, BrowserStorageService]> { const disposables = new DisposableStore(); @@ -25,7 +29,13 @@ async function createStorageService(): Promise<[DisposableStore, BrowserStorageS const userDataProvider = disposables.add(new InMemoryFileSystemProvider()); disposables.add(fileService.registerProvider(Schemas.vscodeUserData, userDataProvider)); - const storageService = disposables.add(new BrowserStorageService({ id: 'workspace-storage-test' }, logService)); + class EnvironmentServiceMock extends mock() { + override readonly userRoamingDataHome = URI.file('/foo').with({ scheme: Schemas.inMemory }); + } + + const userDataProfileService = new UserDataProfilesService(undefined, undefined, new EnvironmentServiceMock(), fileService, new NullLogService()); + + const storageService = disposables.add(new BrowserStorageService({ id: 'workspace-storage-test' }, logService, userDataProfileService)); await storageService.initialize(); @@ -69,6 +79,8 @@ flakySuite('StorageService (browser specific)', () => { test('clear', () => { return runWithFakedTimers({ useFakeTimers: true }, async () => { + storageService.store('bar', 'foo', StorageScope.APPLICATION, StorageTarget.MACHINE); + storageService.store('bar', 3, StorageScope.APPLICATION, StorageTarget.USER); storageService.store('bar', 'foo', StorageScope.GLOBAL, StorageTarget.MACHINE); storageService.store('bar', 3, StorageScope.GLOBAL, StorageTarget.USER); storageService.store('bar', 'foo', StorageScope.WORKSPACE, StorageTarget.MACHINE); @@ -76,7 +88,7 @@ flakySuite('StorageService (browser specific)', () => { await storageService.clear(); - for (const scope of [StorageScope.GLOBAL, StorageScope.WORKSPACE]) { + for (const scope of [StorageScope.APPLICATION, StorageScope.GLOBAL, StorageScope.WORKSPACE]) { for (const target of [StorageTarget.USER, StorageTarget.MACHINE]) { strictEqual(storageService.get('bar', scope), undefined); strictEqual(storageService.keys(scope, target).length, 0); diff --git a/src/vs/platform/storage/test/common/storageService.test.ts b/src/vs/platform/storage/test/common/storageService.test.ts index ea3af86c960..ab5ba71962e 100644 --- a/src/vs/platform/storage/test/common/storageService.test.ts +++ b/src/vs/platform/storage/test/common/storageService.test.ts @@ -18,6 +18,10 @@ export function createSuite(params: { setup: () => Pr return params.teardown(storageService); }); + test('Get Data, Integer, Boolean (application)', () => { + storeData(StorageScope.APPLICATION); + }); + test('Get Data, Integer, Boolean (global)', () => { storeData(StorageScope.GLOBAL); }); @@ -67,6 +71,10 @@ export function createSuite(params: { setup: () => Pr strictEqual(storageService.getBoolean('test.getBooleanDefault', scope, true), true); } + test('Remove Data (application)', () => { + removeData(StorageScope.APPLICATION); + }); + test('Remove Data (global)', () => { removeData(StorageScope.GLOBAL); }); @@ -97,14 +105,14 @@ export function createSuite(params: { setup: () => Pr storageService.onDidChangeValue(e => storageValueChangeEvent = e); // Empty - for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { + for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL, StorageScope.APPLICATION]) { for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { strictEqual(storageService.keys(scope, target).length, 0); } } // Add values - for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { + for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL, StorageScope.APPLICATION]) { for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { storageTargetEvent = Object.create(null); storageValueChangeEvent = Object.create(null); @@ -134,7 +142,7 @@ export function createSuite(params: { setup: () => Pr } // Remove values - for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { + for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL, StorageScope.APPLICATION]) { for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { const keysLength = storageService.keys(scope, target).length; @@ -153,7 +161,7 @@ export function createSuite(params: { setup: () => Pr } // Remove all - for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { + for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL, StorageScope.APPLICATION]) { for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { const keys = storageService.keys(scope, target); @@ -166,7 +174,7 @@ export function createSuite(params: { setup: () => Pr } // Adding undefined or null removes value - for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { + for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL, StorageScope.APPLICATION]) { for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { storageService.store('test.target1', 'value1', scope, target); strictEqual(storageService.keys(scope, target).length, 1); @@ -186,18 +194,20 @@ export function createSuite(params: { setup: () => Pr } // Target change - storageTargetEvent = undefined; - storageService.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE); - ok(storageTargetEvent); - storageTargetEvent = undefined; - storageService.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.USER); - ok(storageTargetEvent); - storageTargetEvent = undefined; - storageService.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE); - ok(storageTargetEvent); - storageTargetEvent = undefined; - storageService.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE); - ok(!storageTargetEvent); // no change in target + for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL, StorageScope.APPLICATION]) { + storageTargetEvent = undefined; + storageService.store('test.target5', 'value1', scope, StorageTarget.MACHINE); + ok(storageTargetEvent); + storageTargetEvent = undefined; + storageService.store('test.target5', 'value1', scope, StorageTarget.USER); + ok(storageTargetEvent); + storageTargetEvent = undefined; + storageService.store('test.target5', 'value1', scope, StorageTarget.MACHINE); + ok(storageTargetEvent); + storageTargetEvent = undefined; + storageService.store('test.target5', 'value1', scope, StorageTarget.MACHINE); + ok(!storageTargetEvent); // no change in target + } }); } diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index ddd09f1eb16..fcd2e70a489 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -6,6 +6,9 @@ import { notStrictEqual, strictEqual } from 'assert'; import { Promises } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; +import { Schemas } from 'vs/base/common/network'; +import { joinPath } from 'vs/base/common/resources'; +import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { OPTIONS, parseArgs } from 'vs/platform/environment/node/argv'; @@ -15,17 +18,29 @@ import { ILifecycleMainService, LifecycleMainPhase, ShutdownEvent, ShutdownReaso import { NullLogService } from 'vs/platform/log/common/log'; import product from 'vs/platform/product/common/product'; import { IProductService } from 'vs/platform/product/common/productService'; -import { IS_NEW_KEY } from 'vs/platform/storage/common/storage'; +import { IS_NEW_KEY, StorageScope } from 'vs/platform/storage/common/storage'; import { IStorageChangeEvent, IStorageMain, IStorageMainOptions } from 'vs/platform/storage/electron-main/storageMain'; import { StorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; import { currentSessionDateStorageKey, firstSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; -import { UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { ICodeWindow, UnloadReason } from 'vs/platform/window/electron-main/window'; suite('StorageMainService', function () { const productService: IProductService = { _serviceBrand: undefined, ...product }; + const inMemoryProfileRoot = URI.file('/location').with({ scheme: Schemas.inMemory }); + const inMemoryProfile: IUserDataProfile = { + name: 'inMemory', + location: inMemoryProfileRoot, + globalStorageHome: joinPath(inMemoryProfileRoot, 'globalStorageHome'), + settingsResource: joinPath(inMemoryProfileRoot, 'settingsResource'), + keybindingsResource: joinPath(inMemoryProfileRoot, 'keybindingsResource'), + tasksResource: joinPath(inMemoryProfileRoot, 'tasksResource'), + snippetsHome: joinPath(inMemoryProfileRoot, 'snippetsHome'), + extensionsResource: joinPath(inMemoryProfileRoot, 'extensionsResource') + }; + class TestStorageMainService extends StorageMainService { protected override getStorageOptions(): IStorageMainOptions { @@ -74,10 +89,10 @@ suite('StorageMainService', function () { async when(phase: LifecycleMainPhase): Promise { } } - async function testStorage(storage: IStorageMain, isGlobal: boolean): Promise { + async function testStorage(storage: IStorageMain, scope: StorageScope): Promise { - // Telemetry: added after init - if (isGlobal) { + // Telemetry: added after init unless workspace scoped + if (scope === StorageScope.APPLICATION || scope === StorageScope.GLOBAL) { strictEqual(storage.items.size, 0); await storage.init(); strictEqual(typeof storage.get(firstSessionDateStorageKey), 'string'); @@ -131,54 +146,77 @@ suite('StorageMainService', function () { return new TestStorageMainService(new NullLogService(), environmentService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, new NullLogService()), lifecycleMainService, fileService); } - test('basics (global)', function () { + test('basics (application)', function () { const storageMainService = createStorageService(); - return testStorage(storageMainService.globalStorage, true); + return testStorage(storageMainService.applicationStorage, StorageScope.APPLICATION); + }); + + test('basics (global)', function () { + const storageMainService = createStorageService(); + const profile = inMemoryProfile; + + return testStorage(storageMainService.globalStorage(profile), StorageScope.GLOBAL); }); test('basics (workspace)', function () { const workspace = { id: generateUuid() }; const storageMainService = createStorageService(); - return testStorage(storageMainService.workspaceStorage(workspace), false); + return testStorage(storageMainService.workspaceStorage(workspace), StorageScope.WORKSPACE); }); test('storage closed onWillShutdown', async function () { const lifecycleMainService = new StorageTestLifecycleMainService(); - const workspace = { id: generateUuid() }; const storageMainService = createStorageService(lifecycleMainService); + const profile = inMemoryProfile; + const workspace = { id: generateUuid() }; + const workspaceStorage = storageMainService.workspaceStorage(workspace); let didCloseWorkspaceStorage = false; workspaceStorage.onDidCloseStorage(() => { didCloseWorkspaceStorage = true; }); - const globalStorage = storageMainService.globalStorage; + const globalStorage = storageMainService.globalStorage(profile); let didCloseGlobalStorage = false; globalStorage.onDidCloseStorage(() => { didCloseGlobalStorage = true; }); + const applicationStorage = storageMainService.applicationStorage; + let didCloseApplicationStorage = false; + applicationStorage.onDidCloseStorage(() => { + didCloseApplicationStorage = true; + }); + + strictEqual(applicationStorage, storageMainService.applicationStorage); // same instance as long as not closed + strictEqual(globalStorage, storageMainService.globalStorage(profile)); // same instance as long as not closed strictEqual(workspaceStorage, storageMainService.workspaceStorage(workspace)); // same instance as long as not closed + await applicationStorage.init(); await globalStorage.init(); await workspaceStorage.init(); await lifecycleMainService.fireOnWillShutdown(); + strictEqual(didCloseApplicationStorage, true); strictEqual(didCloseGlobalStorage, true); strictEqual(didCloseWorkspaceStorage, true); - const storage2 = storageMainService.workspaceStorage(workspace); - notStrictEqual(workspaceStorage, storage2); + const globalStorage2 = storageMainService.globalStorage(profile); + notStrictEqual(globalStorage, globalStorage2); - return storage2.close(); + const workspaceStorage2 = storageMainService.workspaceStorage(workspace); + notStrictEqual(workspaceStorage, workspaceStorage2); + + return workspaceStorage2.close(); }); test('storage closed before init works', async function () { const storageMainService = createStorageService(); + const profile = inMemoryProfile; const workspace = { id: generateUuid() }; const workspaceStorage = storageMainService.workspaceStorage(workspace); @@ -187,21 +225,30 @@ suite('StorageMainService', function () { didCloseWorkspaceStorage = true; }); - const globalStorage = storageMainService.globalStorage; + const globalStorage = storageMainService.globalStorage(profile); let didCloseGlobalStorage = false; globalStorage.onDidCloseStorage(() => { didCloseGlobalStorage = true; }); + const applicationStorage = storageMainService.applicationStorage; + let didCloseApplicationStorage = false; + applicationStorage.onDidCloseStorage(() => { + didCloseApplicationStorage = true; + }); + + await applicationStorage.close(); await globalStorage.close(); await workspaceStorage.close(); + strictEqual(didCloseApplicationStorage, true); strictEqual(didCloseGlobalStorage, true); strictEqual(didCloseWorkspaceStorage, true); }); test('storage closed before init awaits works', async function () { const storageMainService = createStorageService(); + const profile = inMemoryProfile; const workspace = { id: generateUuid() }; const workspaceStorage = storageMainService.workspaceStorage(workspace); @@ -210,18 +257,27 @@ suite('StorageMainService', function () { didCloseWorkspaceStorage = true; }); - const globalStorage = storageMainService.globalStorage; + const globalStorage = storageMainService.globalStorage(profile); let didCloseGlobalStorage = false; globalStorage.onDidCloseStorage(() => { didCloseGlobalStorage = true; }); + const applicationStorage = storageMainService.applicationStorage; + let didCloseApplicationStorage = false; + applicationStorage.onDidCloseStorage(() => { + didCloseApplicationStorage = true; + }); + + applicationStorage.init(); globalStorage.init(); workspaceStorage.init(); + await applicationStorage.close(); await globalStorage.close(); await workspaceStorage.close(); + strictEqual(didCloseApplicationStorage, true); strictEqual(didCloseGlobalStorage, true); strictEqual(didCloseWorkspaceStorage, true); }); diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index 8a0db453476..7cd2aea6dd4 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -29,7 +29,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; import { IProtocolMainService } from 'vs/platform/protocol/electron-main/protocol'; import { resolveMarketplaceHeaders } from 'vs/platform/externalServices/common/marketplace'; -import { IGlobalStorageMainService, IStorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; +import { IApplicationStorageMainService, IStorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; @@ -152,7 +152,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @IPolicyService private readonly policyService: IPolicyService, @IFileService private readonly fileService: IFileService, - @IGlobalStorageMainService private readonly globalStorageMainService: IGlobalStorageMainService, + @IApplicationStorageMainService private readonly applicationStorageMainService: IApplicationStorageMainService, @IStorageMainService private readonly storageMainService: IStorageMainService, @IConfigurationService private readonly configurationService: IConfigurationService, @IThemeMainService private readonly themeMainService: IThemeMainService, @@ -516,7 +516,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { private marketplaceHeadersPromise: Promise | undefined; private getMarketplaceHeaders(): Promise { if (!this.marketplaceHeadersPromise) { - this.marketplaceHeadersPromise = resolveMarketplaceHeaders(this.productService.version, this.productService, this.environmentMainService, this.configurationService, this.fileService, this.globalStorageMainService); + this.marketplaceHeadersPromise = resolveMarketplaceHeaders(this.productService.version, this.productService, this.environmentMainService, this.configurationService, this.fileService, this.applicationStorageMainService); } return this.marketplaceHeadersPromise; diff --git a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts index c4247ff6b0e..0c9f5ee790c 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts @@ -19,7 +19,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { ILogService } from 'vs/platform/log/common/log'; import { StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { IGlobalStorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; +import { IApplicationStorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; import { ICodeWindow } from 'vs/platform/window/electron-main/window'; import { IRecent, IRecentFile, IRecentFolder, IRecentlyOpened, IRecentWorkspace, isRecentFile, isRecentFolder, isRecentWorkspace, restoreRecentlyOpened, toStoreData } from 'vs/platform/workspaces/common/workspaces'; import { isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier, WORKSPACE_EXTENSION } from 'vs/platform/workspace/common/workspace'; @@ -54,7 +54,7 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa @ILogService private readonly logService: ILogService, @IWorkspacesManagementMainService private readonly workspacesManagementMainService: IWorkspacesManagementMainService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, - @IGlobalStorageMainService private readonly globalStorageMainService: IGlobalStorageMainService + @IApplicationStorageMainService private readonly applicationStorageMainService: IApplicationStorageMainService ) { super(); @@ -217,13 +217,13 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa private async getRecentlyOpenedFromStorage(): Promise { - // Wait for global storage to be ready - await this.globalStorageMainService.whenReady; + // Wait for application storage to be ready + await this.applicationStorageMainService.whenReady; let storedRecentlyOpened: object | undefined = undefined; // First try with storage service - const storedRecentlyOpenedRaw = this.globalStorageMainService.get(WorkspacesHistoryMainService.RECENTLY_OPENED_STORAGE_KEY, StorageScope.GLOBAL); + const storedRecentlyOpenedRaw = this.applicationStorageMainService.get(WorkspacesHistoryMainService.RECENTLY_OPENED_STORAGE_KEY, StorageScope.APPLICATION); if (typeof storedRecentlyOpenedRaw === 'string') { try { storedRecentlyOpened = JSON.parse(storedRecentlyOpenedRaw); @@ -237,11 +237,11 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa private async saveRecentlyOpened(recent: IRecentlyOpened): Promise { - // Wait for global storage to be ready - await this.globalStorageMainService.whenReady; + // Wait for application storage to be ready + await this.applicationStorageMainService.whenReady; - // Store in global storage (but do not sync since this is mainly local paths) - this.globalStorageMainService.store(WorkspacesHistoryMainService.RECENTLY_OPENED_STORAGE_KEY, JSON.stringify(toStoreData(recent)), StorageScope.GLOBAL, StorageTarget.MACHINE); + // Store in application storage (but do not sync since this is mainly local paths) + this.applicationStorageMainService.store(WorkspacesHistoryMainService.RECENTLY_OPENED_STORAGE_KEY, JSON.stringify(toStoreData(recent)), StorageScope.APPLICATION, StorageTarget.MACHINE); } private location(recent: IRecent): URI { diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 0a1507fe9fc..5ebd48271c0 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -254,7 +254,7 @@ export class BrowserMain extends Disposable { return service; }), - this.createStorageService(payload, logService).then(service => { + this.createStorageService(payload, logService, userDataProfilesService).then(service => { // Storage serviceCollection.set(IStorageService, service); @@ -426,8 +426,8 @@ export class BrowserMain extends Disposable { }); } - private async createStorageService(payload: IAnyWorkspaceIdentifier, logService: ILogService): Promise { - const storageService = new BrowserStorageService(payload, logService); + private async createStorageService(payload: IAnyWorkspaceIdentifier, logService: ILogService, userDataProfilesService: IUserDataProfilesService): Promise { + const storageService = new BrowserStorageService(payload, logService, userDataProfilesService); try { await storageService.initialize(); diff --git a/src/vs/workbench/common/memento.ts b/src/vs/workbench/common/memento.ts index 2d52437793a..5e756dcf693 100644 --- a/src/vs/workbench/common/memento.ts +++ b/src/vs/workbench/common/memento.ts @@ -11,6 +11,7 @@ export type MementoObject = { [key: string]: any }; export class Memento { + private static readonly applicationMementos = new Map(); private static readonly globalMementos = new Map(); private static readonly workspaceMementos = new Map(); @@ -23,53 +24,56 @@ export class Memento { } getMemento(scope: StorageScope, target: StorageTarget): MementoObject { + switch (scope) { + case StorageScope.APPLICATION: { + let applicationMemento = Memento.applicationMementos.get(this.id); + if (!applicationMemento) { + applicationMemento = new ScopedMemento(this.id, scope, target, this.storageService); + Memento.applicationMementos.set(this.id, applicationMemento); + } - // Scope by Workspace - if (scope === StorageScope.WORKSPACE) { - let workspaceMemento = Memento.workspaceMementos.get(this.id); - if (!workspaceMemento) { - workspaceMemento = new ScopedMemento(this.id, scope, target, this.storageService); - Memento.workspaceMementos.set(this.id, workspaceMemento); + return applicationMemento.getMemento(); } - return workspaceMemento.getMemento(); - } + case StorageScope.GLOBAL: { + let globalMemento = Memento.globalMementos.get(this.id); + if (!globalMemento) { + globalMemento = new ScopedMemento(this.id, scope, target, this.storageService); + Memento.globalMementos.set(this.id, globalMemento); + } - // Scope Global - let globalMemento = Memento.globalMementos.get(this.id); - if (!globalMemento) { - globalMemento = new ScopedMemento(this.id, scope, target, this.storageService); - Memento.globalMementos.set(this.id, globalMemento); - } + return globalMemento.getMemento(); + } - return globalMemento.getMemento(); + case StorageScope.WORKSPACE: { + let workspaceMemento = Memento.workspaceMementos.get(this.id); + if (!workspaceMemento) { + workspaceMemento = new ScopedMemento(this.id, scope, target, this.storageService); + Memento.workspaceMementos.set(this.id, workspaceMemento); + } + + return workspaceMemento.getMemento(); + } + } } saveMemento(): void { - - // Workspace - const workspaceMemento = Memento.workspaceMementos.get(this.id); - if (workspaceMemento) { - workspaceMemento.save(); - } - - // Global - const globalMemento = Memento.globalMementos.get(this.id); - if (globalMemento) { - globalMemento.save(); - } + Memento.applicationMementos.get(this.id)?.save(); + Memento.globalMementos.get(this.id)?.save(); + Memento.workspaceMementos.get(this.id)?.save(); } static clear(scope: StorageScope): void { - - // Workspace - if (scope === StorageScope.WORKSPACE) { - Memento.workspaceMementos.clear(); - } - - // Global - if (scope === StorageScope.GLOBAL) { - Memento.globalMementos.clear(); + switch (scope) { + case StorageScope.APPLICATION: + Memento.applicationMementos.clear(); + break; + case StorageScope.GLOBAL: + Memento.globalMementos.clear(); + break; + case StorageScope.WORKSPACE: + Memento.workspaceMementos.clear(); + break; } } } diff --git a/src/vs/workbench/services/notification/common/notificationService.ts b/src/vs/workbench/services/notification/common/notificationService.ts index ea3df42e4f9..f2af90db01e 100644 --- a/src/vs/workbench/services/notification/common/notificationService.ts +++ b/src/vs/workbench/services/notification/common/notificationService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { INotificationService, INotification, INotificationHandle, Severity, NotificationMessage, INotificationActions, IPromptChoice, IPromptOptions, IStatusMessageOptions, NoOpNotification, NeverShowAgainScope, NotificationsFilter } from 'vs/platform/notification/common/notification'; +import { INotificationService, INotification, INotificationHandle, Severity, NotificationMessage, INotificationActions, IPromptChoice, IPromptOptions, IStatusMessageOptions, NoOpNotification, NeverShowAgainScope, NotificationsFilter, INeverShowAgainOptions } from 'vs/platform/notification/common/notification'; import { NotificationsModel, ChoiceAction, NotificationChangeType } from 'vs/workbench/common/notifications'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; @@ -98,7 +98,7 @@ export class NotificationService extends Disposable implements INotificationServ // Handle neverShowAgain option accordingly if (notification.neverShowAgain) { - const scope = notification.neverShowAgain.scope === NeverShowAgainScope.WORKSPACE ? StorageScope.WORKSPACE : StorageScope.GLOBAL; + const scope = this.toStorageScope(notification.neverShowAgain); const id = notification.neverShowAgain.id; // If the user already picked to not show the notification @@ -142,12 +142,25 @@ export class NotificationService extends Disposable implements INotificationServ return handle; } + private toStorageScope(options: INeverShowAgainOptions): StorageScope { + switch (options.scope) { + case NeverShowAgainScope.APPLICATION: + return StorageScope.APPLICATION; + case NeverShowAgainScope.GLOBAL: + return StorageScope.GLOBAL; + case NeverShowAgainScope.WORKSPACE: + return StorageScope.WORKSPACE; + default: + return StorageScope.GLOBAL; + } + } + prompt(severity: Severity, message: string, choices: IPromptChoice[], options?: IPromptOptions): INotificationHandle { const toDispose = new DisposableStore(); // Handle neverShowAgain option accordingly if (options?.neverShowAgain) { - const scope = options.neverShowAgain.scope === NeverShowAgainScope.WORKSPACE ? StorageScope.WORKSPACE : StorageScope.GLOBAL; + const scope = this.toStorageScope(options.neverShowAgain); const id = options.neverShowAgain.id; // If the user already picked to not show the notification diff --git a/src/vs/workbench/test/common/memento.test.ts b/src/vs/workbench/test/common/memento.test.ts index 2f84c262933..3ba00fa5c6d 100644 --- a/src/vs/workbench/test/common/memento.test.ts +++ b/src/vs/workbench/test/common/memento.test.ts @@ -9,11 +9,11 @@ import { Memento } from 'vs/workbench/common/memento'; import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; suite('Memento', () => { - const context: StorageScope | undefined = undefined; let storage: IStorageService; setup(() => { storage = new TestStorageService(); + Memento.clear(StorageScope.APPLICATION); Memento.clear(StorageScope.GLOBAL); Memento.clear(StorageScope.WORKSPACE); }); @@ -22,8 +22,14 @@ suite('Memento', () => { const myMemento = new Memento('memento.test', storage); // Global - let memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); + let memento = myMemento.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE); memento.foo = [1, 2, 3]; + let applicationMemento = myMemento.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE); + assert.deepStrictEqual(applicationMemento, memento); + + // Global + memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); + memento.foo = [4, 5, 6]; let globalMemento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); assert.deepStrictEqual(globalMemento, memento); @@ -34,62 +40,76 @@ suite('Memento', () => { myMemento.saveMemento(); + // Application + memento = myMemento.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE); + assert.deepStrictEqual(memento, { foo: [1, 2, 3] }); + applicationMemento = myMemento.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE); + assert.deepStrictEqual(applicationMemento, memento); + + // Global + memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); + assert.deepStrictEqual(memento, { foo: [4, 5, 6] }); + globalMemento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); + assert.deepStrictEqual(globalMemento, memento); + + // Workspace + memento = myMemento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE); + assert.deepStrictEqual(memento, { foo: 'Hello World' }); + + // Assert the Mementos are stored properly in storage + assert.deepStrictEqual(JSON.parse(storage.get('memento/memento.test', StorageScope.APPLICATION)!), { foo: [1, 2, 3] }); + assert.deepStrictEqual(JSON.parse(storage.get('memento/memento.test', StorageScope.GLOBAL)!), { foo: [4, 5, 6] }); + assert.deepStrictEqual(JSON.parse(storage.get('memento/memento.test', StorageScope.WORKSPACE)!), { foo: 'Hello World' }); + + // Delete Application + memento = myMemento.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE); + delete memento.foo; + + // Delete Global + memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); + delete memento.foo; + + // Delete Workspace + memento = myMemento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE); + delete memento.foo; + + myMemento.saveMemento(); + + // Application + memento = myMemento.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE); + assert.deepStrictEqual(memento, {}); + + // Global + memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); + assert.deepStrictEqual(memento, {}); + + // Workspace + memento = myMemento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE); + assert.deepStrictEqual(memento, {}); + + // Assert the Mementos are also removed from storage + assert.strictEqual(storage.get('memento/memento.test', StorageScope.APPLICATION, null!), null); + assert.strictEqual(storage.get('memento/memento.test', StorageScope.GLOBAL, null!), null); + assert.strictEqual(storage.get('memento/memento.test', StorageScope.WORKSPACE, null!), null); + }); + + test('Save and Load', () => { + const myMemento = new Memento('memento.test', storage); + + // Global + let memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); + memento.foo = [1, 2, 3]; + + // Workspace + memento = myMemento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE); + assert(memento); + memento.foo = 'Hello World'; + + myMemento.saveMemento(); + // Global memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); assert.deepStrictEqual(memento, { foo: [1, 2, 3] }); - globalMemento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); - assert.deepStrictEqual(globalMemento, memento); - - // Workspace - memento = myMemento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE); - assert.deepStrictEqual(memento, { foo: 'Hello World' }); - - // Assert the Mementos are stored properly in storage - assert.deepStrictEqual(JSON.parse(storage.get('memento/memento.test', StorageScope.GLOBAL)!), { foo: [1, 2, 3] }); - - assert.deepStrictEqual(JSON.parse(storage.get('memento/memento.test', StorageScope.WORKSPACE)!), { foo: 'Hello World' }); - - // Delete Global - memento = myMemento.getMemento(context!, StorageTarget.MACHINE); - delete memento.foo; - - // Delete Workspace - memento = myMemento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE); - delete memento.foo; - - myMemento.saveMemento(); - - // Global - memento = myMemento.getMemento(context!, StorageTarget.MACHINE); - assert.deepStrictEqual(memento, {}); - - // Workspace - memento = myMemento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE); - assert.deepStrictEqual(memento, {}); - - // Assert the Mementos are also removed from storage - assert.strictEqual(storage.get('memento/memento.test', StorageScope.GLOBAL, null!), null); - - assert.strictEqual(storage.get('memento/memento.test', StorageScope.WORKSPACE, null!), null); - }); - - test('Save and Load', () => { - const myMemento = new Memento('memento.test', storage); - - // Global - let memento = myMemento.getMemento(context!, StorageTarget.MACHINE); - memento.foo = [1, 2, 3]; - - // Workspace - memento = myMemento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE); - assert(memento); - memento.foo = 'Hello World'; - - myMemento.saveMemento(); - - // Global - memento = myMemento.getMemento(context!, StorageTarget.MACHINE); - assert.deepStrictEqual(memento, { foo: [1, 2, 3] }); let globalMemento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); assert.deepStrictEqual(globalMemento, memento); @@ -98,7 +118,7 @@ suite('Memento', () => { assert.deepStrictEqual(memento, { foo: 'Hello World' }); // Global - memento = myMemento.getMemento(context!, StorageTarget.MACHINE); + memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); memento.foo = [4, 5, 6]; // Workspace @@ -109,7 +129,7 @@ suite('Memento', () => { myMemento.saveMemento(); // Global - memento = myMemento.getMemento(context!, StorageTarget.MACHINE); + memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); assert.deepStrictEqual(memento, { foo: [4, 5, 6] }); globalMemento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); assert.deepStrictEqual(globalMemento, memento); @@ -119,7 +139,7 @@ suite('Memento', () => { assert.deepStrictEqual(memento, { foo: 'World Hello' }); // Delete Global - memento = myMemento.getMemento(context!, StorageTarget.MACHINE); + memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); delete memento.foo; // Delete Workspace @@ -129,7 +149,7 @@ suite('Memento', () => { myMemento.saveMemento(); // Global - memento = myMemento.getMemento(context!, StorageTarget.MACHINE); + memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); assert.deepStrictEqual(memento, {}); // Workspace @@ -142,10 +162,10 @@ suite('Memento', () => { const myMemento2 = new Memento('memento.test', storage); // Global - let memento = myMemento.getMemento(context!, StorageTarget.MACHINE); + let memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); memento.foo = [1, 2, 3]; - memento = myMemento2.getMemento(context!, StorageTarget.MACHINE); + memento = myMemento2.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); memento.bar = [1, 2, 3]; // Workspace @@ -161,12 +181,12 @@ suite('Memento', () => { myMemento2.saveMemento(); // Global - memento = myMemento.getMemento(context!, StorageTarget.MACHINE); + memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); assert.deepStrictEqual(memento, { foo: [1, 2, 3], bar: [1, 2, 3] }); let globalMemento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); assert.deepStrictEqual(globalMemento, memento); - memento = myMemento2.getMemento(context!, StorageTarget.MACHINE); + memento = myMemento2.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); assert.deepStrictEqual(memento, { foo: [1, 2, 3], bar: [1, 2, 3] }); globalMemento = myMemento2.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); assert.deepStrictEqual(globalMemento, memento); From 0d823bb3fb0b45ce80db9be336310ac92502e03f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 14 Jun 2022 14:37:02 +0200 Subject: [PATCH 012/175] storage - add desktop support --- src/vs/platform/storage/common/storageIpc.ts | 77 +++++++-- .../storage/electron-main/storageIpc.ts | 61 +++++-- .../storage/electron-main/storageMain.ts | 40 ++++- .../electron-main/storageMainService.ts | 159 ++++++++++++------ .../electron-sandbox/storageService.ts | 40 ++++- .../electron-main/storageMainService.test.ts | 1 + 6 files changed, 290 insertions(+), 88 deletions(-) diff --git a/src/vs/platform/storage/common/storageIpc.ts b/src/vs/platform/storage/common/storageIpc.ts index 5774cb82085..8f9c9f6da30 100644 --- a/src/vs/platform/storage/common/storageIpc.ts +++ b/src/vs/platform/storage/common/storageIpc.ts @@ -7,6 +7,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/base/parts/storage/common/storage'; +import { IUserDataProfileDto, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { ISerializedSingleFolderWorkspaceIdentifier, ISerializedWorkspaceIdentifier, IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export type Key = string; @@ -14,6 +15,18 @@ export type Value = string; export type Item = [Key, Value]; export interface IBaseSerializableStorageRequest { + + /** + * Profile to correlate storage. Only used when no + * workspace is provided. Can be undefined to denote + * application scope. + */ + readonly profile: IUserDataProfileDto | undefined; + + /** + * Workspace to correlate storage. Can be undefined to denote + * application or global scope depending on profile. + */ readonly workspace: ISerializedWorkspaceIdentifier | ISerializedSingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined; } @@ -31,19 +44,23 @@ abstract class BaseStorageDatabaseClient extends Disposable implements IStorageD abstract readonly onDidChangeItemsExternal: Event; - constructor(protected channel: IChannel, protected workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined) { + constructor( + protected channel: IChannel, + protected profile: IUserDataProfileDto | undefined, + protected workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined + ) { super(); } async getItems(): Promise> { - const serializableRequest: IBaseSerializableStorageRequest = { workspace: this.workspace }; + const serializableRequest: IBaseSerializableStorageRequest = { profile: this.profile, workspace: this.workspace }; const items: Item[] = await this.channel.call('getItems', serializableRequest); return new Map(items); } updateItems(request: IUpdateRequest): Promise { - const serializableRequest: ISerializableUpdateRequest = { workspace: this.workspace }; + const serializableRequest: ISerializableUpdateRequest = { profile: this.profile, workspace: this.workspace }; if (request.insert) { serializableRequest.insert = Array.from(request.insert.entries()); @@ -59,22 +76,22 @@ abstract class BaseStorageDatabaseClient extends Disposable implements IStorageD abstract close(): Promise; } -class GlobalStorageDatabaseClient extends BaseStorageDatabaseClient implements IStorageDatabase { +abstract class BaseProfileAwareStorageDatabaseClient extends BaseStorageDatabaseClient { private readonly _onDidChangeItemsExternal = this._register(new Emitter()); readonly onDidChangeItemsExternal = this._onDidChangeItemsExternal.event; - constructor(channel: IChannel) { - super(channel, undefined); + constructor(channel: IChannel, profile: IUserDataProfileDto | undefined) { + super(channel, profile, undefined); this.registerListeners(); } private registerListeners(): void { - this._register(this.channel.listen('onDidChangeGlobalStorage')((e: ISerializableItemsChangeEvent) => this.onDidChangeGlobalStorage(e))); + this._register(this.channel.listen('onDidChangeStorage', { profile: this.profile })((e: ISerializableItemsChangeEvent) => this.onDidChangeStorage(e))); } - private onDidChangeGlobalStorage(e: ISerializableItemsChangeEvent): void { + private onDidChangeStorage(e: ISerializableItemsChangeEvent): void { if (Array.isArray(e.changed) || Array.isArray(e.deleted)) { this._onDidChangeItemsExternal.fire({ changed: e.changed ? new Map(e.changed) : undefined, @@ -82,10 +99,17 @@ class GlobalStorageDatabaseClient extends BaseStorageDatabaseClient implements I }); } } +} + +class ApplicationStorageDatabaseClient extends BaseProfileAwareStorageDatabaseClient { + + constructor(channel: IChannel) { + super(channel, undefined); + } async close(): Promise { - // The global storage database is shared across all instances so + // The application storage database is shared across all instances so // we do not close it from the window. However we dispose the // listener for external changes because we no longer interested in it. @@ -93,12 +117,29 @@ class GlobalStorageDatabaseClient extends BaseStorageDatabaseClient implements I } } +class GlobalStorageDatabaseClient extends BaseProfileAwareStorageDatabaseClient { + + constructor(channel: IChannel, profile: IUserDataProfileDto) { + super(channel, profile); + } + + async close(): Promise { + + // The global storage database is shared across all instances of + // the same profile so we do not close it from the window. + // However we dispose the listener for external changes because + // we no longer interested in it. + + this.dispose(); + } +} + class WorkspaceStorageDatabaseClient extends BaseStorageDatabaseClient implements IStorageDatabase { readonly onDidChangeItemsExternal = Event.None; // unsupported for workspace storage because we only ever write from one window - constructor(channel: IChannel, workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier) { - super(channel, workspace); + constructor(channel: IChannel, profile: IUserDataProfileDto, workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier) { + super(channel, profile, workspace); } async close(): Promise { @@ -113,10 +154,19 @@ class WorkspaceStorageDatabaseClient extends BaseStorageDatabaseClient implement export class StorageDatabaseChannelClient extends Disposable { + private _applicationStorage: ApplicationStorageDatabaseClient | undefined = undefined; + get applicationStorage() { + if (!this._applicationStorage) { + this._applicationStorage = new ApplicationStorageDatabaseClient(this.channel); + } + + return this._applicationStorage; + } + private _globalStorage: GlobalStorageDatabaseClient | undefined = undefined; get globalStorage() { if (!this._globalStorage) { - this._globalStorage = new GlobalStorageDatabaseClient(this.channel); + this._globalStorage = new GlobalStorageDatabaseClient(this.channel, this.userDataProfileService.serialize().current); } return this._globalStorage; @@ -125,7 +175,7 @@ export class StorageDatabaseChannelClient extends Disposable { private _workspaceStorage: WorkspaceStorageDatabaseClient | undefined = undefined; get workspaceStorage() { if (!this._workspaceStorage && this.workspace) { - this._workspaceStorage = new WorkspaceStorageDatabaseClient(this.channel, this.workspace); + this._workspaceStorage = new WorkspaceStorageDatabaseClient(this.channel, this.userDataProfileService.serialize().current, this.workspace); } return this._workspaceStorage; @@ -133,6 +183,7 @@ export class StorageDatabaseChannelClient extends Disposable { constructor( private channel: IChannel, + private userDataProfileService: IUserDataProfilesService, private workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined ) { super(); diff --git a/src/vs/platform/storage/electron-main/storageIpc.ts b/src/vs/platform/storage/electron-main/storageIpc.ts index 9802b767d91..b0473d0d53d 100644 --- a/src/vs/platform/storage/electron-main/storageIpc.ts +++ b/src/vs/platform/storage/electron-main/storageIpc.ts @@ -5,19 +5,22 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; +import { revive } from 'vs/base/common/marshalling'; import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { ILogService } from 'vs/platform/log/common/log'; import { IBaseSerializableStorageRequest, ISerializableItemsChangeEvent, ISerializableUpdateRequest, Key, Value } from 'vs/platform/storage/common/storageIpc'; import { IStorageChangeEvent, IStorageMain } from 'vs/platform/storage/electron-main/storageMain'; import { IStorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; +import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { reviveIdentifier, IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export class StorageDatabaseChannel extends Disposable implements IServerChannel { private static readonly STORAGE_CHANGE_DEBOUNCE_TIME = 100; - private readonly _onDidChangeGlobalStorage = this._register(new Emitter()); - private readonly onDidChangeGlobalStorage = this._onDidChangeGlobalStorage.event; + private readonly onDidChangeApplicationStorageEmitter = this._register(new Emitter()); + + private readonly mapProfileToOnDidChangeGlobalStorageEmitter = new Map>(); constructor( private logService: ILogService, @@ -25,16 +28,17 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel ) { super(); - this.registerGlobalStorageListeners(); + this.registerStorageChangeListeners(storageMainService.applicationStorage, this.onDidChangeApplicationStorageEmitter); } - //#region Global Storage Change Events + //#region Storage Change Events - private registerGlobalStorageListeners(): void { + private registerStorageChangeListeners(storage: IStorageMain, emitter: Emitter): void { - // Listen for changes in global storage to send to listeners + // Listen for changes in provided storage to send to listeners // that are listening. Use a debouncer to reduce IPC traffic. - this._register(Event.debounce(this.storageMainService.globalStorage.onDidChangeStorage, (prev: IStorageChangeEvent[] | undefined, cur: IStorageChangeEvent) => { + + this._register(Event.debounce(storage.onDidChangeStorage, (prev: IStorageChangeEvent[] | undefined, cur: IStorageChangeEvent) => { if (!prev) { prev = [cur]; } else { @@ -44,16 +48,16 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel return prev; }, StorageDatabaseChannel.STORAGE_CHANGE_DEBOUNCE_TIME)(events => { if (events.length) { - this._onDidChangeGlobalStorage.fire(this.serializeGlobalStorageEvents(events)); + emitter.fire(this.serializeStorageChangeEvents(events, storage)); } })); } - private serializeGlobalStorageEvents(events: IStorageChangeEvent[]): ISerializableItemsChangeEvent { + private serializeStorageChangeEvents(events: IStorageChangeEvent[], storage: IStorageMain): ISerializableItemsChangeEvent { const changed = new Map(); const deleted = new Set(); events.forEach(event => { - const existing = this.storageMainService.globalStorage.get(event.key); + const existing = storage.get(event.key); if (typeof existing === 'string') { changed.set(event.key, existing); } else { @@ -67,9 +71,26 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel }; } - listen(_: unknown, event: string): Event { + listen(_: unknown, event: string, arg: IBaseSerializableStorageRequest): Event { switch (event) { - case 'onDidChangeGlobalStorage': return this.onDidChangeGlobalStorage; + case 'onDidChangeStorage': { + const profile = arg.profile ? revive(arg.profile) : undefined; + + // Without profile: application scope + if (!profile) { + return this.onDidChangeApplicationStorageEmitter.event; + } + + // With profile: global scope for the profile + let globalStorageChangeEmitter = this.mapProfileToOnDidChangeGlobalStorageEmitter.get(profile.id); + if (!globalStorageChangeEmitter) { + globalStorageChangeEmitter = this._register(new Emitter()); + this.registerStorageChangeListeners(this.storageMainService.globalStorage(profile), globalStorageChangeEmitter); + this.mapProfileToOnDidChangeGlobalStorageEmitter.set(profile.id, globalStorageChangeEmitter); + } + + return globalStorageChangeEmitter.event; + } } throw new Error(`Event not found: ${event}`); @@ -78,10 +99,11 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel //#endregion async call(_: unknown, command: string, arg: IBaseSerializableStorageRequest): Promise { + const profile = arg.profile ? revive(arg.profile) : undefined; const workspace = reviveIdentifier(arg.workspace); // Get storage to be ready - const storage = await this.withStorageInitialized(workspace); + const storage = await this.withStorageInitialized(profile, workspace); // handle call switch (command) { @@ -110,13 +132,20 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel } } - private async withStorageInitialized(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined): Promise { - const storage = workspace ? this.storageMainService.workspaceStorage(workspace) : this.storageMainService.globalStorage; + private async withStorageInitialized(profile: IUserDataProfile | undefined, workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined): Promise { + let storage: IStorageMain; + if (workspace) { + storage = this.storageMainService.workspaceStorage(workspace); + } else if (profile) { + storage = this.storageMainService.globalStorage(profile); + } else { + storage = this.storageMainService.applicationStorage; + } try { await storage.init(); } catch (error) { - this.logService.error(`StorageIPC#init: Unable to init ${workspace ? 'workspace' : 'global'} storage due to ${error}`); + this.logService.error(`StorageIPC#init: Unable to init ${workspace ? 'workspace' : profile ? 'global' : 'application'} storage due to ${error}`); } return storage; diff --git a/src/vs/platform/storage/electron-main/storageMain.ts b/src/vs/platform/storage/electron-main/storageMain.ts index 7faf5ee69cd..bb6984061aa 100644 --- a/src/vs/platform/storage/electron-main/storageMain.ts +++ b/src/vs/platform/storage/electron-main/storageMain.ts @@ -17,7 +17,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IFileService } from 'vs/platform/files/common/files'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { IS_NEW_KEY } from 'vs/platform/storage/common/storage'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { currentSessionDateStorageKey, firstSessionDateStorageKey, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; import { IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; @@ -31,8 +31,8 @@ export interface IStorageMainOptions { } /** - * Provides access to global and workspace storage from the - * electron-main side that is the owner of all storage connections. + * Provides access to application, global and workspace storage from + * the electron-main side that is the owner of all storage connections. */ export interface IStorageMain extends IDisposable { @@ -255,22 +255,22 @@ abstract class BaseStorageMain extends Disposable implements IStorageMain { } } -export class GlobalStorageMain extends BaseStorageMain implements IStorageMain { +class BaseProfileAwareStorageMain extends BaseStorageMain { private static readonly STORAGE_NAME = 'state.vscdb'; get path(): string | undefined { if (!this.options.useInMemoryStorage) { - return join(this.userDataProfilesService.defaultProfile.globalStorageHome.fsPath, GlobalStorageMain.STORAGE_NAME); + return join(this.profile.globalStorageHome.fsPath, BaseProfileAwareStorageMain.STORAGE_NAME); } return undefined; } constructor( + private readonly profile: IUserDataProfile, private readonly options: IStorageMainOptions, logService: ILogService, - private readonly userDataProfilesService: IUserDataProfilesService, fileService: IFileService ) { super(logService, fileService); @@ -285,7 +285,7 @@ export class GlobalStorageMain extends BaseStorageMain implements IStorageMain { protected override async doInit(storage: IStorage): Promise { await super.doInit(storage); - // Apply global telemetry values as part of the initialization + // Apply telemetry values as part of the initialization this.updateTelemetryState(storage); } @@ -307,7 +307,31 @@ export class GlobalStorageMain extends BaseStorageMain implements IStorageMain { } } -export class WorkspaceStorageMain extends BaseStorageMain implements IStorageMain { +export class GlobalStorageMain extends BaseProfileAwareStorageMain { + + constructor( + profile: IUserDataProfile, + options: IStorageMainOptions, + logService: ILogService, + fileService: IFileService + ) { + super(profile, options, logService, fileService); + } +} + +export class ApplicationStorageMain extends BaseProfileAwareStorageMain { + + constructor( + options: IStorageMainOptions, + userDataProfileService: IUserDataProfilesService, + logService: ILogService, + fileService: IFileService + ) { + super(userDataProfileService.defaultProfile, options, logService, fileService); + } +} + +export class WorkspaceStorageMain extends BaseStorageMain { private static readonly WORKSPACE_STORAGE_NAME = 'state.vscdb'; private static readonly WORKSPACE_META_NAME = 'workspace.json'; diff --git a/src/vs/platform/storage/electron-main/storageMainService.ts b/src/vs/platform/storage/electron-main/storageMainService.ts index 3c5e483fcb0..af8ad0ce3de 100644 --- a/src/vs/platform/storage/electron-main/storageMainService.ts +++ b/src/vs/platform/storage/electron-main/storageMainService.ts @@ -12,8 +12,8 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { ILifecycleMainService, LifecycleMainPhase, ShutdownReason } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { ILogService } from 'vs/platform/log/common/log'; import { AbstractStorageService, IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { GlobalStorageMain, InMemoryStorageMain, IStorageMain, IStorageMainOptions, WorkspaceStorageMain } from 'vs/platform/storage/electron-main/storageMain'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { ApplicationStorageMain, GlobalStorageMain, InMemoryStorageMain, IStorageMain, IStorageMainOptions, WorkspaceStorageMain } from 'vs/platform/storage/electron-main/storageMain'; +import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IAnyWorkspaceIdentifier, IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; //#region Storage Main Service (intent: make global and workspace storage accessible to windows from main process) @@ -25,12 +25,22 @@ export interface IStorageMainService { readonly _serviceBrand: undefined; /** - * Provides access to the global storage shared across all windows. + * Provides access to the application storage shared across all + * windows and all profiles. * * Note: DO NOT use this for reading/writing from the main process! - * Rather use `IGlobalStorageMainService` for that purpose. + * Rather use `IApplicationStorageMainService` for that purpose. */ - readonly globalStorage: IStorageMain; + applicationStorage: IStorageMain; + + /** + * Provides access to the global storage shared across all windows + * for the provided profile. + * + * Note: DO NOT use this for reading/writing from the main process! + * This is currently not supported. + */ + globalStorage(profile: IUserDataProfile): IStorageMain; /** * Provides access to the workspace storage specific to a single window. @@ -67,15 +77,21 @@ export class StorageMainService extends Disposable implements IStorageMainServic private registerListeners(): void { - // Global Storage: Warmup when any window opens + // Application Storage: Warmup when any window opens (async () => { await this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen); - this.globalStorage.init(); + this.applicationStorage.init(); })(); - // Workspace Storage: Warmup when related window with workspace loads this._register(this.lifecycleMainService.onWillLoadWindow(e => { + + // Global Storage: Warmup when related window with profile loads + if (e.window.profile) { + this.globalStorage(e.window.profile).init(); + } + + // Workspace Storage: Warmup when related window with workspace loads if (e.workspace) { this.workspaceStorage(e.workspace).init(); } @@ -88,38 +104,84 @@ export class StorageMainService extends Disposable implements IStorageMainServic // Remember shutdown reason this.shutdownReason = e.reason; - // Global Storage - e.join(this.globalStorage.close()); + // Application Storage + e.join(this.applicationStorage.close()); + + // Global Storage(s) + for (const [, globalStorage] of this.mapProfileToStorage) { + e.join(globalStorage.close()); + } // Workspace Storage(s) - for (const [, storage] of this.mapWorkspaceToStorage) { - e.join(storage.close()); + for (const [, workspaceStorage] of this.mapWorkspaceToStorage) { + e.join(workspaceStorage.close()); } })); } - //#region Global Storage + //#region Application Storage - readonly globalStorage = this.createGlobalStorage(); + readonly applicationStorage = this.createApplicationStorage(); - private createGlobalStorage(): IStorageMain { - this.logService.trace(`StorageMainService: creating global storage`); + private createApplicationStorage(): IStorageMain { + this.logService.trace(`StorageMainService: creating application storage`); - const globalStorage = new GlobalStorageMain(this.getStorageOptions(), this.logService, this.userDataProfilesService, this.fileService); + const applicationStorage = new ApplicationStorageMain(this.getStorageOptions(), this.userDataProfilesService, this.logService, this.fileService); - once(globalStorage.onDidCloseStorage)(() => { - this.logService.trace(`StorageMainService: closed global storage`); + once(applicationStorage.onDidCloseStorage)(() => { + this.logService.trace(`StorageMainService: closed application storage`); }); + return applicationStorage; + } + + //#endregion + + //#region Global Storage + + private readonly mapProfileToStorage = new Map(); + + globalStorage(profile: IUserDataProfile): IStorageMain { + if (profile.id === this.userDataProfilesService.defaultProfile.id) { + return this.applicationStorage; // for default profile, use application storage + } + + let globalStorage = this.mapProfileToStorage.get(profile.id); + if (!globalStorage) { + this.logService.trace(`StorageMainService: creating global storage (${profile.name})`); + + globalStorage = this.createGlobalStorage(profile); + this.mapProfileToStorage.set(profile.id, globalStorage); + + once(globalStorage.onDidCloseStorage)(() => { + this.logService.trace(`StorageMainService: closed global storage (${profile.name})`); + + this.mapProfileToStorage.delete(profile.id); + }); + } + return globalStorage; } + private createGlobalStorage(profile: IUserDataProfile): IStorageMain { + if (this.shutdownReason === ShutdownReason.KILL) { + + // Workaround for native crashes that we see when + // SQLite DBs are being created even after shutdown + // https://github.com/microsoft/vscode/issues/143186 + + return new InMemoryStorageMain(this.logService, this.fileService); + } + + return new GlobalStorageMain(profile, this.getStorageOptions(), this.logService, this.fileService); + } + //#endregion //#region Workspace Storage - private readonly mapWorkspaceToStorage = new Map(); + private readonly mapWorkspaceToStorage = new Map(); workspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier): IStorageMain { let workspaceStorage = this.mapWorkspaceToStorage.get(workspace.id); @@ -158,15 +220,15 @@ export class StorageMainService extends Disposable implements IStorageMainServic //#endregion -//#region Global Main Storage Service (intent: use global storage from main process) +//#region Application Main Storage Service (intent: use application storage from main process) -export const IGlobalStorageMainService = createDecorator('globalStorageMainService'); +export const IApplicationStorageMainService = createDecorator('applicationStorageMainService'); /** * A specialized `IStorageService` interface that only allows - * access to the `StorageScope.GLOBAL` scope. + * access to the `StorageScope.APPLICATION` scope. */ -export interface IGlobalStorageMainService extends IStorageService { +export interface IApplicationStorageMainService extends IStorageService { /** * Important: unlike other storage services in the renderer, the @@ -174,38 +236,38 @@ export interface IGlobalStorageMainService extends IStorageService { * storage is being initialized while a window opens to reduce * pressure on startup. * - * As such, any client wanting to access global storage from the + * As such, any client wanting to access application storage from the * main process needs to wait for `whenReady`, otherwise there is * a chance that the service operates on an in-memory store that * is not backed by any persistent DB. */ readonly whenReady: Promise; - get(key: string, scope: StorageScope.GLOBAL, fallbackValue: string): string; - get(key: string, scope: StorageScope.GLOBAL, fallbackValue?: string): string | undefined; + get(key: string, scope: StorageScope.APPLICATION, fallbackValue: string): string; + get(key: string, scope: StorageScope.APPLICATION, fallbackValue?: string): string | undefined; - getBoolean(key: string, scope: StorageScope.GLOBAL, fallbackValue: boolean): boolean; - getBoolean(key: string, scope: StorageScope.GLOBAL, fallbackValue?: boolean): boolean | undefined; + getBoolean(key: string, scope: StorageScope.APPLICATION, fallbackValue: boolean): boolean; + getBoolean(key: string, scope: StorageScope.APPLICATION, fallbackValue?: boolean): boolean | undefined; - getNumber(key: string, scope: StorageScope.GLOBAL, fallbackValue: number): number; - getNumber(key: string, scope: StorageScope.GLOBAL, fallbackValue?: number): number | undefined; + getNumber(key: string, scope: StorageScope.APPLICATION, fallbackValue: number): number; + getNumber(key: string, scope: StorageScope.APPLICATION, fallbackValue?: number): number | undefined; - store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope.GLOBAL, target: StorageTarget): void; + store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope.APPLICATION, target: StorageTarget): void; - remove(key: string, scope: StorageScope.GLOBAL): void; + remove(key: string, scope: StorageScope.APPLICATION): void; - keys(scope: StorageScope.GLOBAL, target: StorageTarget): string[]; + keys(scope: StorageScope.APPLICATION, target: StorageTarget): string[]; migrate(toWorkspace: IAnyWorkspaceIdentifier): never; - isNew(scope: StorageScope.GLOBAL): boolean; + isNew(scope: StorageScope.APPLICATION): boolean; } -export class GlobalStorageMainService extends AbstractStorageService implements IGlobalStorageMainService { +export class ApplicationStorageMainService extends AbstractStorageService implements IApplicationStorageMainService { declare readonly _serviceBrand: undefined; - readonly whenReady = this.storageMainService.globalStorage.whenInit; + readonly whenReady = this.storageMainService.applicationStorage.whenInit; constructor( @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @@ -216,23 +278,26 @@ export class GlobalStorageMainService extends AbstractStorageService implements protected doInitialize(): Promise { - // global storage is being initialized as part - // of the first window opening, so we do not - // trigger it here but can join it - return this.storageMainService.globalStorage.whenInit; + // application storage is being initialized as part + // of the first window opening, so we do not trigger + // it here but can join it + return this.storageMainService.applicationStorage.whenInit; } protected getStorage(scope: StorageScope): IStorage | undefined { - switch (scope) { - case StorageScope.GLOBAL: - return this.storageMainService.globalStorage.storage; - case StorageScope.WORKSPACE: - return undefined; // unsupported from main process + if (scope === StorageScope.APPLICATION) { + return this.storageMainService.applicationStorage.storage; } + + return undefined; // any other scope is unsupported from main process } protected getLogDetails(scope: StorageScope): string | undefined { - return scope === StorageScope.GLOBAL ? this.userDataProfilesService.defaultProfile.globalStorageHome.fsPath : undefined; + if (scope === StorageScope.APPLICATION) { + return this.userDataProfilesService.defaultProfile.globalStorageHome.fsPath; + } + + return undefined; // any other scope is unsupported from main process } protected override shouldFlushWhenIdle(): boolean { diff --git a/src/vs/platform/storage/electron-sandbox/storageService.ts b/src/vs/platform/storage/electron-sandbox/storageService.ts index fd69b47c141..00fa288c4fa 100644 --- a/src/vs/platform/storage/electron-sandbox/storageService.ts +++ b/src/vs/platform/storage/electron-sandbox/storageService.ts @@ -16,7 +16,12 @@ import { IAnyWorkspaceIdentifier, IEmptyWorkspaceIdentifier, ISingleFolderWorksp export class NativeStorageService extends AbstractStorageService { + // Application Storage is readonly and shared across + // windows and profiles. + private readonly applicationStorage: IStorage; + // Global Storage is readonly and shared across windows + // under the same profile. private readonly globalStorage: IStorage; // Workspace Storage is scoped to a window but can change @@ -33,12 +38,23 @@ export class NativeStorageService extends AbstractStorageService { ) { super(); + this.applicationStorage = this.createApplicationStorage(); this.globalStorage = this.createGlobalStorage(); this.workspaceStorage = this.createWorkspaceStorage(workspace); } + private createApplicationStorage(): IStorage { + const storageDataBaseClient = new StorageDatabaseChannelClient(this.mainProcessService.getChannel('storage'), this.userDataProfilesService, undefined); + + const applicationStorage = new Storage(storageDataBaseClient.applicationStorage); + + this._register(applicationStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.APPLICATION, key))); + + return applicationStorage; + } + private createGlobalStorage(): IStorage { - const storageDataBaseClient = new StorageDatabaseChannelClient(this.mainProcessService.getChannel('storage'), undefined); + const storageDataBaseClient = new StorageDatabaseChannelClient(this.mainProcessService.getChannel('storage'), this.userDataProfilesService, undefined); const globalStorage = new Storage(storageDataBaseClient.globalStorage); @@ -50,7 +66,7 @@ export class NativeStorageService extends AbstractStorageService { private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier): IStorage; private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined): IStorage | undefined; private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined): IStorage | undefined { - const storageDataBaseClient = new StorageDatabaseChannelClient(this.mainProcessService.getChannel('storage'), workspace); + const storageDataBaseClient = new StorageDatabaseChannelClient(this.mainProcessService.getChannel('storage'), this.userDataProfilesService, workspace); if (storageDataBaseClient.workspaceStorage) { const workspaceStorage = new Storage(storageDataBaseClient.workspaceStorage); @@ -71,17 +87,32 @@ export class NativeStorageService extends AbstractStorageService { // Init all storage locations await Promises.settled([ + this.applicationStorage.init(), this.globalStorage.init(), this.workspaceStorage?.init() ?? Promise.resolve() ]); } protected getStorage(scope: StorageScope): IStorage | undefined { - return scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage; + switch (scope) { + case StorageScope.APPLICATION: + return this.applicationStorage; + case StorageScope.GLOBAL: + return this.globalStorage; + default: + return this.workspaceStorage; + } } protected getLogDetails(scope: StorageScope): string | undefined { - return scope === StorageScope.GLOBAL ? this.userDataProfilesService.defaultProfile.globalStorageHome.fsPath : this.workspaceStorageId ? `${joinPath(this.environmentService.workspaceStorageHome, this.workspaceStorageId, 'state.vscdb').fsPath}` : undefined; + switch (scope) { + case StorageScope.APPLICATION: + return this.userDataProfilesService.defaultProfile.globalStorageHome.fsPath; + case StorageScope.GLOBAL: + return this.userDataProfilesService.currentProfile.globalStorageHome.fsPath; + default: + return this.workspaceStorageId ? `${joinPath(this.environmentService.workspaceStorageHome, this.workspaceStorageId, 'state.vscdb').fsPath}` : undefined; + } } async close(): Promise { @@ -94,6 +125,7 @@ export class NativeStorageService extends AbstractStorageService { // Do it await Promises.settled([ + this.applicationStorage.close(), this.globalStorage.close(), this.workspaceStorage?.close() ?? Promise.resolve() ]); diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index fcd2e70a489..dfa53fe215f 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -31,6 +31,7 @@ suite('StorageMainService', function () { const inMemoryProfileRoot = URI.file('/location').with({ scheme: Schemas.inMemory }); const inMemoryProfile: IUserDataProfile = { + id: 'id', name: 'inMemory', location: inMemoryProfileRoot, globalStorageHome: joinPath(inMemoryProfileRoot, 'globalStorageHome'), From 031ab70d48d9189e079bbbf0cd014a42d989f23e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 14 Jun 2022 14:46:26 +0200 Subject: [PATCH 013/175] - introduce isDefault property on profile - reuse application storage for global for default profile in web --- src/vs/base/browser/indexedDB.ts | 2 +- .../storage/browser/storageService.ts | 29 +++++++++++-------- .../electron-main/storageMainService.test.ts | 1 + .../userDataProfile/common/userDataProfile.ts | 4 +++ .../electron-browser/workbenchTestServices.ts | 1 + 5 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/vs/base/browser/indexedDB.ts b/src/vs/base/browser/indexedDB.ts index 5fba9c0ab4c..25651932124 100644 --- a/src/vs/base/browser/indexedDB.ts +++ b/src/vs/base/browser/indexedDB.ts @@ -28,7 +28,7 @@ export class IndexedDB { return new IndexedDB(database, name); } - static async openDatabase(name: string, version: number | undefined, stores: string[]): Promise { + private static async openDatabase(name: string, version: number | undefined, stores: string[]): Promise { mark(`code/willOpenDatabase/${name}`); try { return await IndexedDB.doOpenDatabase(name, version, stores); diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts index f8ed2d1c5c8..be5d10fce81 100644 --- a/src/vs/platform/storage/browser/storageService.ts +++ b/src/vs/platform/storage/browser/storageService.ts @@ -9,8 +9,6 @@ import { Promises } from 'vs/base/common/async'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { hash } from 'vs/base/common/hash'; -import { isEqual } from 'vs/base/common/resources'; import { InMemoryStorageDatabase, isStorageItemsChangeEvent, IStorage, IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest, Storage } from 'vs/base/parts/storage/common/storage'; import { ILogService } from 'vs/platform/log/common/log'; import { AbstractStorageService, IS_NEW_KEY, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; @@ -50,10 +48,10 @@ export class BrowserStorageService extends AbstractStorageService { case StorageScope.APPLICATION: return 'global'; // use the default profile global DB for application scope case StorageScope.GLOBAL: - if (isEqual(this.userDataProfileService.currentProfile.location, this.userDataProfileService.defaultProfile.location)) { + if (this.userDataProfileService.currentProfile.isDefault) { return 'global'; // default profile DB has a fixed name for backwards compatibility } else { - return `global-${hash(this.userDataProfileService.currentProfile.location.toString()).toString(16)}`; + return `global-${this.userDataProfileService.currentProfile.id}`; } case StorageScope.WORKSPACE: return this.payload.id; @@ -63,11 +61,14 @@ export class BrowserStorageService extends AbstractStorageService { protected async doInitialize(): Promise { // Create Storage in Parallel - const [applicationStorageDatabase, globalStorageDatabase, workspaceStorageDatabase] = await Promises.settled([ - IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.APPLICATION), broadcastChanges: true }, this.logService), - IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.GLOBAL), broadcastChanges: true }, this.logService), - IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.WORKSPACE) }, this.logService) - ]); + const promises: Promise[] = []; + promises.push(IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.APPLICATION), broadcastChanges: true }, this.logService)); + promises.push(IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.WORKSPACE) }, this.logService)); + // Create global storage only if the current profile is not a default profie otherwise we use the application storage + if (!this.userDataProfileService.currentProfile.isDefault) { + promises.push(IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.GLOBAL), broadcastChanges: true }, this.logService)); + } + const [applicationStorageDatabase, workspaceStorageDatabase, globalStorageDatabase] = await Promises.settled(promises); // Workspace Storage this.workspaceStorageDatabase = this._register(workspaceStorageDatabase); @@ -80,11 +81,15 @@ export class BrowserStorageService extends AbstractStorageService { this._register(this.applicationStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.APPLICATION, key))); // Global Storage - this.globalStorageDatabase = this._register(globalStorageDatabase); - this.globalStorage = this._register(new Storage(this.globalStorageDatabase)); + if (globalStorageDatabase) { + this.globalStorageDatabase = this._register(globalStorageDatabase); + this.globalStorage = this._register(new Storage(this.globalStorageDatabase)); + } else { + this.globalStorage = this.applicationStorage; + } this._register(this.globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); - // Init both + // Init storages await Promises.settled([ this.workspaceStorage.init(), this.globalStorage.init(), diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index dfa53fe215f..3cdfb0ea46e 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -33,6 +33,7 @@ suite('StorageMainService', function () { const inMemoryProfile: IUserDataProfile = { id: 'id', name: 'inMemory', + isDefault: false, location: inMemoryProfileRoot, globalStorageHome: joinPath(inMemoryProfileRoot, 'globalStorageHome'), settingsResource: joinPath(inMemoryProfileRoot, 'settingsResource'), diff --git a/src/vs/platform/userDataProfile/common/userDataProfile.ts b/src/vs/platform/userDataProfile/common/userDataProfile.ts index d10dd672e4d..b916e7510be 100644 --- a/src/vs/platform/userDataProfile/common/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/common/userDataProfile.ts @@ -17,6 +17,7 @@ import { ILogService } from 'vs/platform/log/common/log'; export interface IUserDataProfile { readonly id: string; + readonly isDefault: boolean; readonly name: string; readonly location: URI; readonly globalStorageHome: URI; @@ -53,6 +54,7 @@ export interface IUserDataProfilesService { function reviveProfile(profile: IUserDataProfile, scheme: string): IUserDataProfile { return { id: profile.id, + isDefault: profile.isDefault, name: profile.name, location: URI.revive(profile.location).with({ scheme }), globalStorageHome: URI.revive(profile.globalStorageHome).with({ scheme }), @@ -93,9 +95,11 @@ export class UserDataProfilesService extends Disposable implements IUserDataProf } createProfile(name: string | undefined): IUserDataProfile { + const isDefault = !name || name === UserDataProfilesService.DEFAULT_PROFILE_NAME; const location = name && name !== UserDataProfilesService.DEFAULT_PROFILE_NAME ? joinPath(this.profilesHome, name) : this.environmentService.userRoamingDataHome; return { id: hash(location.toString()).toString(16), + isDefault, name: name ?? UserDataProfilesService.DEFAULT_PROFILE_NAME, location, globalStorageHome: joinPath(location, 'globalStorage'), diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index 1738a1903dc..79d2fefb506 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -58,6 +58,7 @@ const homeDir = homedir(); const NULL_PROFILE = { name: '', id: '', + isDefault: false, location: URI.file(homeDir), settingsResource: joinPath(URI.file(homeDir), 'settings.json'), globalStorageHome: joinPath(URI.file(homeDir), 'globalStorage'), From 364355c4af3904e2d6063fb6d3d9679bc9c4f1ac Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 14 Jun 2022 15:02:48 +0200 Subject: [PATCH 014/175] :lipstick: --- src/vs/workbench/common/memento.ts | 40 ++++++++++++++++-------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/common/memento.ts b/src/vs/workbench/common/memento.ts index 5e756dcf693..1c506756d1a 100644 --- a/src/vs/workbench/common/memento.ts +++ b/src/vs/workbench/common/memento.ts @@ -25,16 +25,19 @@ export class Memento { getMemento(scope: StorageScope, target: StorageTarget): MementoObject { switch (scope) { - case StorageScope.APPLICATION: { - let applicationMemento = Memento.applicationMementos.get(this.id); - if (!applicationMemento) { - applicationMemento = new ScopedMemento(this.id, scope, target, this.storageService); - Memento.applicationMementos.set(this.id, applicationMemento); + + // Scope by Workspace + case StorageScope.WORKSPACE: { + let workspaceMemento = Memento.workspaceMementos.get(this.id); + if (!workspaceMemento) { + workspaceMemento = new ScopedMemento(this.id, scope, target, this.storageService); + Memento.workspaceMementos.set(this.id, workspaceMemento); } - return applicationMemento.getMemento(); + return workspaceMemento.getMemento(); } + // Scope Global case StorageScope.GLOBAL: { let globalMemento = Memento.globalMementos.get(this.id); if (!globalMemento) { @@ -45,34 +48,35 @@ export class Memento { return globalMemento.getMemento(); } - case StorageScope.WORKSPACE: { - let workspaceMemento = Memento.workspaceMementos.get(this.id); - if (!workspaceMemento) { - workspaceMemento = new ScopedMemento(this.id, scope, target, this.storageService); - Memento.workspaceMementos.set(this.id, workspaceMemento); + // Scope Application + case StorageScope.APPLICATION: { + let applicationMemento = Memento.applicationMementos.get(this.id); + if (!applicationMemento) { + applicationMemento = new ScopedMemento(this.id, scope, target, this.storageService); + Memento.applicationMementos.set(this.id, applicationMemento); } - return workspaceMemento.getMemento(); + return applicationMemento.getMemento(); } } } saveMemento(): void { - Memento.applicationMementos.get(this.id)?.save(); - Memento.globalMementos.get(this.id)?.save(); Memento.workspaceMementos.get(this.id)?.save(); + Memento.globalMementos.get(this.id)?.save(); + Memento.applicationMementos.get(this.id)?.save(); } static clear(scope: StorageScope): void { switch (scope) { - case StorageScope.APPLICATION: - Memento.applicationMementos.clear(); + case StorageScope.WORKSPACE: + Memento.workspaceMementos.clear(); break; case StorageScope.GLOBAL: Memento.globalMementos.clear(); break; - case StorageScope.WORKSPACE: - Memento.workspaceMementos.clear(); + case StorageScope.APPLICATION: + Memento.applicationMementos.clear(); break; } } From 0d15a3034b785b0a7c05434816245398b865b6df Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 14 Jun 2022 15:16:44 +0200 Subject: [PATCH 015/175] remove need for profile on `WorkspaceStorageDatabaseClient` --- src/vs/platform/storage/common/storageIpc.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/storage/common/storageIpc.ts b/src/vs/platform/storage/common/storageIpc.ts index 8f9c9f6da30..98dc8ea35cc 100644 --- a/src/vs/platform/storage/common/storageIpc.ts +++ b/src/vs/platform/storage/common/storageIpc.ts @@ -138,8 +138,8 @@ class WorkspaceStorageDatabaseClient extends BaseStorageDatabaseClient implement readonly onDidChangeItemsExternal = Event.None; // unsupported for workspace storage because we only ever write from one window - constructor(channel: IChannel, profile: IUserDataProfileDto, workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier) { - super(channel, profile, workspace); + constructor(channel: IChannel, workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier) { + super(channel, undefined, workspace); } async close(): Promise { @@ -175,7 +175,7 @@ export class StorageDatabaseChannelClient extends Disposable { private _workspaceStorage: WorkspaceStorageDatabaseClient | undefined = undefined; get workspaceStorage() { if (!this._workspaceStorage && this.workspace) { - this._workspaceStorage = new WorkspaceStorageDatabaseClient(this.channel, this.userDataProfileService.serialize().current, this.workspace); + this._workspaceStorage = new WorkspaceStorageDatabaseClient(this.channel, this.workspace); } return this._workspaceStorage; From 5b3668c7c2f763752b832c893c718e80e1352122 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 14 Jun 2022 15:19:08 +0200 Subject: [PATCH 016/175] adopt `isDefault` --- src/vs/platform/storage/electron-main/storageMainService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/storage/electron-main/storageMainService.ts b/src/vs/platform/storage/electron-main/storageMainService.ts index af8ad0ce3de..64f0815e794 100644 --- a/src/vs/platform/storage/electron-main/storageMainService.ts +++ b/src/vs/platform/storage/electron-main/storageMainService.ts @@ -142,7 +142,7 @@ export class StorageMainService extends Disposable implements IStorageMainServic private readonly mapProfileToStorage = new Map(); globalStorage(profile: IUserDataProfile): IStorageMain { - if (profile.id === this.userDataProfilesService.defaultProfile.id) { + if (profile.isDefault) { return this.applicationStorage; // for default profile, use application storage } From d590f467798f40b0bba90cb9736f699cc104648c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 14 Jun 2022 15:30:51 +0200 Subject: [PATCH 017/175] access current profile directly --- src/vs/platform/storage/common/storageIpc.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/storage/common/storageIpc.ts b/src/vs/platform/storage/common/storageIpc.ts index 98dc8ea35cc..41a1f23f68e 100644 --- a/src/vs/platform/storage/common/storageIpc.ts +++ b/src/vs/platform/storage/common/storageIpc.ts @@ -166,7 +166,7 @@ export class StorageDatabaseChannelClient extends Disposable { private _globalStorage: GlobalStorageDatabaseClient | undefined = undefined; get globalStorage() { if (!this._globalStorage) { - this._globalStorage = new GlobalStorageDatabaseClient(this.channel, this.userDataProfileService.serialize().current); + this._globalStorage = new GlobalStorageDatabaseClient(this.channel, this.userDataProfileService.currentProfile); } return this._globalStorage; From 1dc0d64cba464a40de01b60cb450cc5dc908d8c4 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 14 Jun 2022 15:56:14 +0200 Subject: [PATCH 018/175] fix browser storage tests --- .../test/browser/storageService.test.ts | 37 +++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/storage/test/browser/storageService.test.ts b/src/vs/platform/storage/test/browser/storageService.test.ts index 56c76e70f35..91bd5f6ebed 100644 --- a/src/vs/platform/storage/test/browser/storageService.test.ts +++ b/src/vs/platform/storage/test/browser/storageService.test.ts @@ -6,6 +6,7 @@ import { strictEqual } from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; +import { joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { Storage } from 'vs/base/parts/storage/common/storage'; import { mock } from 'vs/base/test/common/mock'; @@ -18,7 +19,7 @@ import { NullLogService } from 'vs/platform/log/common/log'; import { BrowserStorageService, IndexedDBStorageDatabase } from 'vs/platform/storage/browser/storageService'; import { StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { createSuite } from 'vs/platform/storage/test/common/storageService.test'; -import { UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; async function createStorageService(): Promise<[DisposableStore, BrowserStorageService]> { const disposables = new DisposableStore(); @@ -29,11 +30,41 @@ async function createStorageService(): Promise<[DisposableStore, BrowserStorageS const userDataProvider = disposables.add(new InMemoryFileSystemProvider()); disposables.add(fileService.registerProvider(Schemas.vscodeUserData, userDataProvider)); + const profilesRoot = URI.file('/profiles').with({ scheme: Schemas.inMemory }); + class EnvironmentServiceMock extends mock() { - override readonly userRoamingDataHome = URI.file('/foo').with({ scheme: Schemas.inMemory }); + override readonly userRoamingDataHome = profilesRoot; } - const userDataProfileService = new UserDataProfilesService(undefined, undefined, new EnvironmentServiceMock(), fileService, new NullLogService()); + const inMemoryDefaultProfileRoot = joinPath(profilesRoot, 'default'); + const inMemoryDefaultProfile: IUserDataProfile = { + id: 'id', + name: 'inMemory', + isDefault: true, + location: inMemoryDefaultProfileRoot, + globalStorageHome: joinPath(inMemoryDefaultProfileRoot, 'globalStorageHome'), + settingsResource: joinPath(inMemoryDefaultProfileRoot, 'settingsResource'), + keybindingsResource: joinPath(inMemoryDefaultProfileRoot, 'keybindingsResource'), + tasksResource: joinPath(inMemoryDefaultProfileRoot, 'tasksResource'), + snippetsHome: joinPath(inMemoryDefaultProfileRoot, 'snippetsHome'), + extensionsResource: joinPath(inMemoryDefaultProfileRoot, 'extensionsResource') + }; + + const inMemoryExtraProfileRoot = joinPath(profilesRoot, 'extra'); + const inMemoryExtraProfile: IUserDataProfile = { + id: 'id', + name: 'inMemory', + isDefault: false, + location: inMemoryExtraProfileRoot, + globalStorageHome: joinPath(inMemoryExtraProfileRoot, 'globalStorageHome'), + settingsResource: joinPath(inMemoryExtraProfileRoot, 'settingsResource'), + keybindingsResource: joinPath(inMemoryExtraProfileRoot, 'keybindingsResource'), + tasksResource: joinPath(inMemoryExtraProfileRoot, 'tasksResource'), + snippetsHome: joinPath(inMemoryExtraProfileRoot, 'snippetsHome'), + extensionsResource: joinPath(inMemoryExtraProfileRoot, 'extensionsResource') + }; + + const userDataProfileService = new UserDataProfilesService(inMemoryDefaultProfile, inMemoryExtraProfile, new EnvironmentServiceMock(), fileService, new NullLogService()); const storageService = disposables.add(new BrowserStorageService({ id: 'workspace-storage-test' }, logService, userDataProfileService)); From dd96bda20e0775632d7a77941dac543f704b108f Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 14 Jun 2022 16:02:54 +0200 Subject: [PATCH 019/175] adopt application scope --- src/vs/platform/externalServices/common/serviceMachineId.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/externalServices/common/serviceMachineId.ts b/src/vs/platform/externalServices/common/serviceMachineId.ts index e2e17317b5e..b6422b8b25a 100644 --- a/src/vs/platform/externalServices/common/serviceMachineId.ts +++ b/src/vs/platform/externalServices/common/serviceMachineId.ts @@ -10,7 +10,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; export async function getServiceMachineId(environmentService: IEnvironmentService, fileService: IFileService, storageService: IStorageService | undefined): Promise { - let uuid: string | null = storageService ? storageService.get('storage.serviceMachineId', StorageScope.GLOBAL) || null : null; + let uuid: string | null = storageService ? storageService.get('storage.serviceMachineId', StorageScope.APPLICATION) || null : null; if (uuid) { return uuid; } @@ -31,7 +31,7 @@ export async function getServiceMachineId(environmentService: IEnvironmentServic } } if (storageService) { - storageService.store('storage.serviceMachineId', uuid, StorageScope.GLOBAL, StorageTarget.MACHINE); + storageService.store('storage.serviceMachineId', uuid, StorageScope.APPLICATION, StorageTarget.MACHINE); } return uuid; } From a2e25e976258524ca2b266f6d7ac5efb4ace3dfb Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 14 Jun 2022 16:04:27 +0200 Subject: [PATCH 020/175] set some main side storage things as application scoped --- .../storage/electron-main/storageMain.ts | 48 +++++++++---------- .../electron-main/storageMainService.test.ts | 4 +- .../experiments/common/experimentService.ts | 2 +- .../browser/gettingStarted.ts | 2 +- .../browser/workbenchCommonProperties.ts | 8 ++-- .../workbenchCommonProperties.ts | 4 +- .../electron-browser/commonProperties.test.ts | 2 +- 7 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/vs/platform/storage/electron-main/storageMain.ts b/src/vs/platform/storage/electron-main/storageMain.ts index bb6984061aa..b4d9ab4256e 100644 --- a/src/vs/platform/storage/electron-main/storageMain.ts +++ b/src/vs/platform/storage/electron-main/storageMain.ts @@ -281,30 +281,6 @@ class BaseProfileAwareStorageMain extends BaseStorageMain { logging: this.createLoggingOptions() })); } - - protected override async doInit(storage: IStorage): Promise { - await super.doInit(storage); - - // Apply telemetry values as part of the initialization - this.updateTelemetryState(storage); - } - - private updateTelemetryState(storage: IStorage): void { - - // First session date (once) - const firstSessionDate = storage.get(firstSessionDateStorageKey, undefined); - if (firstSessionDate === undefined) { - storage.set(firstSessionDateStorageKey, new Date().toUTCString()); - } - - // Last / current session (always) - // previous session date was the "current" one at that time - // current session date is "now" - const lastSessionDate = storage.get(currentSessionDateStorageKey, undefined); - const currentSessionDate = new Date().toUTCString(); - storage.set(lastSessionDateStorageKey, typeof lastSessionDate === 'undefined' ? null : lastSessionDate); - storage.set(currentSessionDateStorageKey, currentSessionDate); - } } export class GlobalStorageMain extends BaseProfileAwareStorageMain { @@ -329,6 +305,30 @@ export class ApplicationStorageMain extends BaseProfileAwareStorageMain { ) { super(userDataProfileService.defaultProfile, options, logService, fileService); } + + protected override async doInit(storage: IStorage): Promise { + await super.doInit(storage); + + // Apply telemetry values as part of the application storage initialization + this.updateTelemetryState(storage); + } + + private updateTelemetryState(storage: IStorage): void { + + // First session date (once) + const firstSessionDate = storage.get(firstSessionDateStorageKey, undefined); + if (firstSessionDate === undefined) { + storage.set(firstSessionDateStorageKey, new Date().toUTCString()); + } + + // Last / current session (always) + // previous session date was the "current" one at that time + // current session date is "now" + const lastSessionDate = storage.get(currentSessionDateStorageKey, undefined); + const currentSessionDate = new Date().toUTCString(); + storage.set(lastSessionDateStorageKey, typeof lastSessionDate === 'undefined' ? null : lastSessionDate); + storage.set(currentSessionDateStorageKey, currentSessionDate); + } } export class WorkspaceStorageMain extends BaseStorageMain { diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index 3cdfb0ea46e..63292faaff4 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -93,8 +93,8 @@ suite('StorageMainService', function () { async function testStorage(storage: IStorageMain, scope: StorageScope): Promise { - // Telemetry: added after init unless workspace scoped - if (scope === StorageScope.APPLICATION || scope === StorageScope.GLOBAL) { + // Telemetry: added after init unless workspace/global scoped + if (scope === StorageScope.APPLICATION) { strictEqual(storage.items.size, 0); await storage.init(); strictEqual(typeof storage.get(firstSessionDateStorageKey), 'string'); diff --git a/src/vs/workbench/contrib/experiments/common/experimentService.ts b/src/vs/workbench/contrib/experiments/common/experimentService.ts index 07741ba7717..c66a550ef63 100644 --- a/src/vs/workbench/contrib/experiments/common/experimentService.ts +++ b/src/vs/workbench/contrib/experiments/common/experimentService.ts @@ -483,7 +483,7 @@ export class ExperimentService extends Disposable implements IExperimentService return Promise.resolve(ExperimentState.NoRun); } - const isNewUser = !this.storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL); + const isNewUser = !this.storageService.get(lastSessionDateStorageKey, StorageScope.APPLICATION); if ((condition.newUser === true && !isNewUser) || (condition.newUser === false && isNewUser)) { return Promise.resolve(ExperimentState.NoRun); diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts index 5fac78668bf..0c1cd126ccc 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts @@ -815,7 +815,7 @@ export class GettingStartedPage extends EditorPane { this.buildTelemetryFooter(telemetryNotice); footer.appendChild(telemetryNotice); } else if (!this.productService.openToWelcomeMainPage && !someStepsComplete && !this.hasScrolledToFirstCategory) { - const firstSessionDateString = this.storageService.get(firstSessionDateStorageKey, StorageScope.GLOBAL) || new Date().toUTCString(); + const firstSessionDateString = this.storageService.get(firstSessionDateStorageKey, StorageScope.APPLICATION) || new Date().toUTCString(); const daysSinceFirstSession = ((+new Date()) - (+new Date(firstSessionDateString))) / 1000 / 60 / 60 / 24; const fistContentBehaviour = daysSinceFirstSession < 1 ? 'openToFirstCategory' : 'index'; diff --git a/src/vs/workbench/services/telemetry/browser/workbenchCommonProperties.ts b/src/vs/workbench/services/telemetry/browser/workbenchCommonProperties.ts index 69116d9a458..16bce794a23 100644 --- a/src/vs/workbench/services/telemetry/browser/workbenchCommonProperties.ts +++ b/src/vs/workbench/services/telemetry/browser/workbenchCommonProperties.ts @@ -30,15 +30,15 @@ export async function resolveWorkbenchCommonProperties( resolveAdditionalProperties?: () => { [key: string]: any } ): Promise<{ [name: string]: string | undefined }> { const result: { [name: string]: string | undefined } = Object.create(null); - const firstSessionDate = storageService.get(firstSessionDateStorageKey, StorageScope.GLOBAL)!; - const lastSessionDate = storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL)!; + const firstSessionDate = storageService.get(firstSessionDateStorageKey, StorageScope.APPLICATION)!; + const lastSessionDate = storageService.get(lastSessionDateStorageKey, StorageScope.APPLICATION)!; let machineId: string | undefined; if (!removeMachineId) { - machineId = storageService.get(machineIdKey, StorageScope.GLOBAL); + machineId = storageService.get(machineIdKey, StorageScope.APPLICATION); if (!machineId) { machineId = uuid.generateUuid(); - storageService.store(machineIdKey, machineId, StorageScope.GLOBAL, StorageTarget.MACHINE); + storageService.store(machineIdKey, machineId, StorageScope.APPLICATION, StorageTarget.MACHINE); } } else { machineId = `Redacted-${productIdentifier ?? 'web'}`; diff --git a/src/vs/workbench/services/telemetry/electron-sandbox/workbenchCommonProperties.ts b/src/vs/workbench/services/telemetry/electron-sandbox/workbenchCommonProperties.ts index ff36a5231e6..9d5b4282308 100644 --- a/src/vs/workbench/services/telemetry/electron-sandbox/workbenchCommonProperties.ts +++ b/src/vs/workbench/services/telemetry/electron-sandbox/workbenchCommonProperties.ts @@ -23,8 +23,8 @@ export async function resolveWorkbenchCommonProperties( remoteAuthority?: string ): Promise<{ [name: string]: string | boolean | undefined }> { const result = await resolveCommonProperties(fileService, release, hostname, process.arch, commit, version, machineId, msftInternalDomains, installSourcePath); - const firstSessionDate = storageService.get(firstSessionDateStorageKey, StorageScope.GLOBAL)!; - const lastSessionDate = storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL)!; + const firstSessionDate = storageService.get(firstSessionDateStorageKey, StorageScope.APPLICATION)!; + const lastSessionDate = storageService.get(lastSessionDateStorageKey, StorageScope.APPLICATION)!; // __GDPR__COMMON__ "common.version.shell" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } result['common.version.shell'] = process.versions['electron']; diff --git a/src/vs/workbench/services/telemetry/test/electron-browser/commonProperties.test.ts b/src/vs/workbench/services/telemetry/test/electron-browser/commonProperties.test.ts index ac596534940..8ae51addd3e 100644 --- a/src/vs/workbench/services/telemetry/test/electron-browser/commonProperties.test.ts +++ b/src/vs/workbench/services/telemetry/test/electron-browser/commonProperties.test.ts @@ -72,7 +72,7 @@ suite('Telemetry - common properties', function () { test('lastSessionDate when available', async function () { - testStorageService.store('telemetry.lastSessionDate', new Date().toUTCString(), StorageScope.GLOBAL, StorageTarget.MACHINE); + testStorageService.store('telemetry.lastSessionDate', new Date().toUTCString(), StorageScope.APPLICATION, StorageTarget.MACHINE); const props = await resolveWorkbenchCommonProperties(testStorageService, testFileService, release(), hostname(), commit, version, 'someMachineId', undefined, installSource); assert.ok('common.lastSessionDate' in props); // conditional, see below From adf9340be6801cd5988806fc3051d9da9f367ab4 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 14 Jun 2022 18:19:00 -0700 Subject: [PATCH 021/175] Try prevent duplicate placeholder decorations showing This adds these protections: - Placeholders are now always disposed of before a new one is created, this was the main reason for these issues. - Prevent the command started event firing if it has already fired on the same line. Fixes #151228 --- .../common/capabilities/commandDetectionCapability.ts | 6 ++++++ .../contrib/terminal/browser/xterm/decorationAddon.ts | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts index f9a14291b1f..a67d3fdfc31 100644 --- a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts +++ b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts @@ -297,6 +297,12 @@ export class CommandDetectionCapability implements ICommandDetectionCapability { } handleCommandStart(): void { + // Only update the column if the line has already been set + if (this._currentCommand.commandStartMarker?.line === this._terminal.buffer.active.cursorY) { + this._currentCommand.commandStartX = this._terminal.buffer.active.cursorX; + this._logService.debug('CommandDetectionCapability#handleCommandStart', this._currentCommand.commandStartX, this._currentCommand.commandStartMarker?.line); + return; + } if (this._isWindowsPty) { this._handleCommandStartWindows(); return; diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts index 2b346c110b9..b45bfa99f6d 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts @@ -215,14 +215,14 @@ export class DecorationAddon extends Disposable implements ITerminalAddon { if (!decoration) { return undefined; } - + if (beforeCommandExecution) { + this._placeholderDecoration = decoration; + } decoration.onRender(element => { if (element.classList.contains(DecorationSelector.OverviewRuler)) { return; } - if (beforeCommandExecution && !this._placeholderDecoration) { - this._placeholderDecoration = decoration; - } else if (!this._decorations.get(decoration.marker.id)) { + if (!this._decorations.get(decoration.marker.id)) { decoration.onDispose(() => this._decorations.delete(decoration.marker.id)); this._decorations.set(decoration.marker.id, { From 26d5de29615748fc113aa3abae5cfeab57a11fa6 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 14 Jun 2022 18:20:14 -0700 Subject: [PATCH 022/175] Set default icon for test tasks to beaker Fixes #152057 --- .../contrib/tasks/browser/terminalTaskSystem.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index 5946cc37dd0..9515d758635 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -52,6 +52,8 @@ import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/b import { INotificationService } from 'vs/platform/notification/common/notification'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { formatMessageForTerminal } from 'vs/platform/terminal/common/terminalStrings'; +import { GroupKind } from 'vs/workbench/contrib/tasks/common/taskConfiguration'; +import { Codicon } from 'vs/base/common/codicons'; interface ITerminalData { terminal: ITerminalInstance; @@ -1029,13 +1031,21 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { os, remoteAuthority: this._environmentService.remoteAuthority }); + let icon: URI | ThemeIcon | { light: URI; dark: URI } | undefined; + if (task.configurationProperties.icon) { + icon = ThemeIcon.fromId(task.configurationProperties.icon); + } else { + const taskGroupKind = task.configurationProperties.group ? GroupKind.to(task.configurationProperties.group) : undefined; + const kindId = typeof taskGroupKind === 'string' ? taskGroupKind : taskGroupKind?.kind; + icon = kindId === 'test' ? ThemeIcon.fromId(Codicon.beaker.id) : defaultProfile.icon; + } shellLaunchConfig = { name: terminalName, type, executable: defaultProfile.path, args: defaultProfile.args, env: { ...defaultProfile.env }, - icon: task.configurationProperties.icon ? ThemeIcon.fromId(task.configurationProperties.icon) : defaultProfile.icon, + icon, color: task.configurationProperties.color || defaultProfile.color, waitOnExit }; From 0b74e15affc1b44a5b7cd671dd0a02d3ed6d51a7 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 15 Jun 2022 06:56:26 +0200 Subject: [PATCH 023/175] ux - dont use grab cursor (fixes #151918) (#152146) --- .../workbench/browser/parts/editor/media/titlecontrol.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css index 1237c847967..378d2bfa63f 100644 --- a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css @@ -5,6 +5,10 @@ /* Editor Label */ +.monaco-workbench .part.editor > .content .editor-group-container > .title { + cursor: pointer; +} + .monaco-workbench .part.editor > .content .editor-group-container > .title .title-label, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label { white-space: nowrap; @@ -36,10 +40,6 @@ /* Drag and Drop */ -.monaco-workbench .part.editor > .content .editor-group-container > .title { - cursor: grab; -} - .monaco-editor-group-drag-image { display: inline-block; padding: 1px 7px; From cdaf61edab9ac5f87f84d88edfb8611fc6ce67cf Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 15 Jun 2022 08:31:56 +0200 Subject: [PATCH 024/175] :lipstick: --- .../notification/common/notification.ts | 3 +- .../storage/browser/storageService.ts | 5 +- src/vs/platform/storage/common/storage.ts | 46 +++++++++++++++---- src/vs/platform/storage/common/storageIpc.ts | 4 +- .../electron-main/storageMainService.ts | 2 +- .../experiments/common/experimentService.ts | 4 +- .../common/notificationService.ts | 2 +- 7 files changed, 48 insertions(+), 18 deletions(-) diff --git a/src/vs/platform/notification/common/notification.ts b/src/vs/platform/notification/common/notification.ts index 5c4fa815ab7..612aa179426 100644 --- a/src/vs/platform/notification/common/notification.ts +++ b/src/vs/platform/notification/common/notification.ts @@ -72,7 +72,8 @@ export interface INeverShowAgainOptions { /** * Whether to persist the choice in the current workspace or for all workspaces. By - * default it will be persisted for all workspaces (= `NeverShowAgainScope.GLOBAL`). + * default it will be persisted for all workspaces across all profiles + * (= `NeverShowAgainScope.APPLICATION`). */ readonly scope?: NeverShowAgainScope; } diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts index be5d10fce81..677e3ca90aa 100644 --- a/src/vs/platform/storage/browser/storageService.ts +++ b/src/vs/platform/storage/browser/storageService.ts @@ -64,8 +64,11 @@ export class BrowserStorageService extends AbstractStorageService { const promises: Promise[] = []; promises.push(IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.APPLICATION), broadcastChanges: true }, this.logService)); promises.push(IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.WORKSPACE) }, this.logService)); - // Create global storage only if the current profile is not a default profie otherwise we use the application storage if (!this.userDataProfileService.currentProfile.isDefault) { + + // Create global storage only if the current profile is not a + // default profie otherwise we use the application storage + promises.push(IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.GLOBAL), broadcastChanges: true }, this.logService)); } const [applicationStorageDatabase, workspaceStorageDatabase, globalStorageDatabase] = await Promises.settled(promises); diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts index ececf161093..9061792183d 100644 --- a/src/vs/platform/storage/common/storage.ts +++ b/src/vs/platform/storage/common/storage.ts @@ -31,7 +31,7 @@ export enum WillSaveStateReason { } export interface IWillSaveStateEvent { - reason: WillSaveStateReason; + readonly reason: WillSaveStateReason; } export interface IStorageService { @@ -522,12 +522,15 @@ export abstract class AbstractStorageService extends Disposable implements IStor } async logStorage(): Promise { + const applicationItems = this.getStorage(StorageScope.APPLICATION)?.items ?? new Map(); const globalItems = this.getStorage(StorageScope.GLOBAL)?.items ?? new Map(); const workspaceItems = this.getStorage(StorageScope.WORKSPACE)?.items ?? new Map(); return logStorage( + applicationItems, globalItems, workspaceItems, + this.getLogDetails(StorageScope.APPLICATION) ?? '', this.getLogDetails(StorageScope.GLOBAL) ?? '', this.getLogDetails(StorageScope.WORKSPACE) ?? '' ); @@ -553,9 +556,9 @@ export class InMemoryStorageService extends AbstractStorageService { constructor() { super(); - this._register(this.applicationStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.APPLICATION, key))); - this._register(this.globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); this._register(this.workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key))); + this._register(this.globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); + this._register(this.applicationStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.APPLICATION, key))); } protected getStorage(scope: StorageScope): IStorage { @@ -587,7 +590,7 @@ export class InMemoryStorageService extends AbstractStorageService { } } -export async function logStorage(global: Map, workspace: Map, globalPath: string, workspacePath: string): Promise { +export async function logStorage(application: Map, global: Map, workspace: Map, applicationPath: string, globalPath: string, workspacePath: string): Promise { const safeParse = (value: string) => { try { return JSON.parse(value); @@ -596,6 +599,13 @@ export async function logStorage(global: Map, workspace: Map(); + const applicationItemsParsed = new Map(); + application.forEach((value, key) => { + applicationItems.set(key, value); + applicationItemsParsed.set(key, safeParse(value)); + }); + const globalItems = new Map(); const globalItemsParsed = new Map(); global.forEach((value, key) => { @@ -610,15 +620,31 @@ export async function logStorage(global: Map, workspace: Map { - globalValues.push({ key, value }); + if (applicationPath !== globalPath) { + console.group(`Storage: Application (path: ${applicationPath})`); + } else { + console.group(`Storage: Application & Global (path: ${applicationPath}, default profile)`); + } + const applicationValues: { key: string; value: string }[] = []; + applicationItems.forEach((value, key) => { + applicationValues.push({ key, value }); }); - console.table(globalValues); + console.table(applicationValues); console.groupEnd(); - console.log(globalItemsParsed); + console.log(applicationItemsParsed); + + if (applicationPath !== globalPath) { + console.group(`Storage: Global (path: ${globalPath}, profile specific)`); + const globalValues: { key: string; value: string }[] = []; + globalItems.forEach((value, key) => { + globalValues.push({ key, value }); + }); + console.table(globalValues); + console.groupEnd(); + + console.log(globalItemsParsed); + } console.group(`Storage: Workspace (path: ${workspacePath})`); const workspaceValues: { key: string; value: string }[] = []; diff --git a/src/vs/platform/storage/common/storageIpc.ts b/src/vs/platform/storage/common/storageIpc.ts index 41a1f23f68e..e61a2736999 100644 --- a/src/vs/platform/storage/common/storageIpc.ts +++ b/src/vs/platform/storage/common/storageIpc.ts @@ -24,8 +24,8 @@ export interface IBaseSerializableStorageRequest { readonly profile: IUserDataProfileDto | undefined; /** - * Workspace to correlate storage. Can be undefined to denote - * application or global scope depending on profile. + * Workspace to correlate storage. Can be undefined to + * denote application or global scope depending on profile. */ readonly workspace: ISerializedWorkspaceIdentifier | ISerializedSingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined; } diff --git a/src/vs/platform/storage/electron-main/storageMainService.ts b/src/vs/platform/storage/electron-main/storageMainService.ts index 64f0815e794..e1770742e7e 100644 --- a/src/vs/platform/storage/electron-main/storageMainService.ts +++ b/src/vs/platform/storage/electron-main/storageMainService.ts @@ -16,7 +16,7 @@ import { ApplicationStorageMain, GlobalStorageMain, InMemoryStorageMain, IStorag import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IAnyWorkspaceIdentifier, IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; -//#region Storage Main Service (intent: make global and workspace storage accessible to windows from main process) +//#region Storage Main Service (intent: make application, global and workspace storage accessible to windows from main process) export const IStorageMainService = createDecorator('storageMainService'); diff --git a/src/vs/workbench/contrib/experiments/common/experimentService.ts b/src/vs/workbench/contrib/experiments/common/experimentService.ts index c66a550ef63..f6e71c6c793 100644 --- a/src/vs/workbench/contrib/experiments/common/experimentService.ts +++ b/src/vs/workbench/contrib/experiments/common/experimentService.ts @@ -19,7 +19,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { IProductService } from 'vs/platform/product/common/productService'; import { asJson, IRequestService } from 'vs/platform/request/common/request'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { ITelemetryService, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceTagsService } from 'vs/workbench/contrib/tags/common/workspaceTags'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -483,7 +483,7 @@ export class ExperimentService extends Disposable implements IExperimentService return Promise.resolve(ExperimentState.NoRun); } - const isNewUser = !this.storageService.get(lastSessionDateStorageKey, StorageScope.APPLICATION); + const isNewUser = this.storageService.isNew(StorageScope.APPLICATION); if ((condition.newUser === true && !isNewUser) || (condition.newUser === false && isNewUser)) { return Promise.resolve(ExperimentState.NoRun); diff --git a/src/vs/workbench/services/notification/common/notificationService.ts b/src/vs/workbench/services/notification/common/notificationService.ts index f2af90db01e..f618b5e9ce0 100644 --- a/src/vs/workbench/services/notification/common/notificationService.ts +++ b/src/vs/workbench/services/notification/common/notificationService.ts @@ -151,7 +151,7 @@ export class NotificationService extends Disposable implements INotificationServ case NeverShowAgainScope.WORKSPACE: return StorageScope.WORKSPACE; default: - return StorageScope.GLOBAL; + return StorageScope.APPLICATION; } } From efb5563dcff194eb7bfb2075d1910e0d96d82e37 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 15 Jun 2022 08:45:44 +0200 Subject: [PATCH 025/175] revert experiment service change --- .../workbench/contrib/experiments/common/experimentService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/experiments/common/experimentService.ts b/src/vs/workbench/contrib/experiments/common/experimentService.ts index f6e71c6c793..c66a550ef63 100644 --- a/src/vs/workbench/contrib/experiments/common/experimentService.ts +++ b/src/vs/workbench/contrib/experiments/common/experimentService.ts @@ -19,7 +19,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { IProductService } from 'vs/platform/product/common/productService'; import { asJson, IRequestService } from 'vs/platform/request/common/request'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ITelemetryService, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceTagsService } from 'vs/workbench/contrib/tags/common/workspaceTags'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -483,7 +483,7 @@ export class ExperimentService extends Disposable implements IExperimentService return Promise.resolve(ExperimentState.NoRun); } - const isNewUser = this.storageService.isNew(StorageScope.APPLICATION); + const isNewUser = !this.storageService.get(lastSessionDateStorageKey, StorageScope.APPLICATION); if ((condition.newUser === true && !isNewUser) || (condition.newUser === false && isNewUser)) { return Promise.resolve(ExperimentState.NoRun); From a4906e241e4de51e59a86504ce80d0fae0f7892d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 15 Jun 2022 08:56:16 +0200 Subject: [PATCH 026/175] dont create storage in default profile twice --- .../storage/electron-sandbox/storageService.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/storage/electron-sandbox/storageService.ts b/src/vs/platform/storage/electron-sandbox/storageService.ts index 00fa288c4fa..893dfbcb552 100644 --- a/src/vs/platform/storage/electron-sandbox/storageService.ts +++ b/src/vs/platform/storage/electron-sandbox/storageService.ts @@ -45,7 +45,6 @@ export class NativeStorageService extends AbstractStorageService { private createApplicationStorage(): IStorage { const storageDataBaseClient = new StorageDatabaseChannelClient(this.mainProcessService.getChannel('storage'), this.userDataProfilesService, undefined); - const applicationStorage = new Storage(storageDataBaseClient.applicationStorage); this._register(applicationStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.APPLICATION, key))); @@ -54,9 +53,20 @@ export class NativeStorageService extends AbstractStorageService { } private createGlobalStorage(): IStorage { - const storageDataBaseClient = new StorageDatabaseChannelClient(this.mainProcessService.getChannel('storage'), this.userDataProfilesService, undefined); + let globalStorage: IStorage; - const globalStorage = new Storage(storageDataBaseClient.globalStorage); + if (this.userDataProfilesService.currentProfile.isDefault) { + + // If we are in default profile, the global storage is + // actually the same as application storage. As such we + // avoid creating the storage library a second time on + // the same DB. + + globalStorage = this.applicationStorage; + } else { + const storageDataBaseClient = new StorageDatabaseChannelClient(this.mainProcessService.getChannel('storage'), this.userDataProfilesService, undefined); + globalStorage = new Storage(storageDataBaseClient.globalStorage); + } this._register(globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); From ee1f284f5f0e4d0212f6c6406c55ebe0c8ac3162 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 15 Jun 2022 09:03:54 +0200 Subject: [PATCH 027/175] comment --- src/vs/platform/storage/browser/storageService.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts index 677e3ca90aa..63b7cf9e7b2 100644 --- a/src/vs/platform/storage/browser/storageService.ts +++ b/src/vs/platform/storage/browser/storageService.ts @@ -66,8 +66,10 @@ export class BrowserStorageService extends AbstractStorageService { promises.push(IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.WORKSPACE) }, this.logService)); if (!this.userDataProfileService.currentProfile.isDefault) { - // Create global storage only if the current profile is not a - // default profie otherwise we use the application storage + // If we are in default profile, the global storage is + // actually the same as application storage. As such we + // avoid creating the storage library a second time on + // the same DB. promises.push(IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.GLOBAL), broadcastChanges: true }, this.logService)); } From 917f6978e9d0aa2a979cfd14865b3f8e63b67b8f Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 15 Jun 2022 09:54:31 +0200 Subject: [PATCH 028/175] - implement profile CRUD operations - enable profile actions in dev mode --- .../sharedProcess/sharedProcessMain.ts | 3 +- src/vs/code/electron-main/main.ts | 13 +- .../electron-main/sharedProcess.ts | 2 +- .../sharedProcess/node/sharedProcess.ts | 5 +- src/vs/platform/storage/common/storageIpc.ts | 11 +- .../userDataProfile/common/userDataProfile.ts | 116 ++++++++--------- .../electron-main/userDataProfile.ts | 123 +++++++++++++++--- .../electron-sandbox/userDataProfile.ts | 23 +++- src/vs/platform/window/common/window.ts | 8 +- .../platform/windows/electron-main/window.ts | 7 +- .../electron-main/windowsMainService.ts | 5 +- .../browser/userDataProfile.contribution.ts | 13 +- .../common/userDataProfileActions.ts | 89 +++++++++++-- .../browser/userDataProfileManagement.ts | 95 +++++++++----- .../userDataProfile/common/userDataProfile.ts | 3 +- 15 files changed, 359 insertions(+), 157 deletions(-) diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index bd7c26a8616..918ec2e5e40 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -102,7 +102,6 @@ import { IExtensionsScannerService } from 'vs/platform/extensionManagement/commo import { ExtensionsScannerService } from 'vs/platform/extensionManagement/node/extensionsScannerService'; import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { ExtensionsProfileScannerService, IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; -import { revive } from 'vs/base/common/marshalling'; import { PolicyChannelClient } from 'vs/platform/policy/common/policyIpc'; import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; import { OneDataSystemAppender } from 'vs/platform/telemetry/node/1dsAppender'; @@ -233,7 +232,7 @@ class SharedProcessMain extends Disposable { fileService.registerProvider(Schemas.vscodeUserData, userDataFileSystemProvider); // User Data Profiles - const userDataProfilesService = this._register(new UserDataProfilesService(revive(this.configuration.profiles.default), revive(this.configuration.profiles.current), environmentService, fileService, logService)); + const userDataProfilesService = this._register(new UserDataProfilesService(this.configuration.defaultProfile, undefined, environmentService, fileService, logService)); services.set(IUserDataProfilesService, userDataProfilesService); // Configuration diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 28db082ca0d..d846dcb1304 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -68,6 +68,8 @@ import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/pol import { NativePolicyService } from 'vs/platform/policy/node/nativePolicyService'; import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; /** * The main VS Code entry point. @@ -170,6 +172,10 @@ class CodeMain { const diskFileSystemProvider = new DiskFileSystemProvider(logService); fileService.registerProvider(Schemas.file, diskFileSystemProvider); + // URI Identity + const uriIdentityService = new UriIdentityService(fileService); + services.set(IUriIdentityService, uriIdentityService); + // Logger services.set(ILoggerService, new LoggerService(logService, fileService)); @@ -178,7 +184,7 @@ class CodeMain { services.set(IStateMainService, stateMainService); // User Data Profiles - const userDataProfilesMainService = new UserDataProfilesMainService(stateMainService, environmentMainService, fileService, logService); + const userDataProfilesMainService = new UserDataProfilesMainService(stateMainService, uriIdentityService, environmentMainService, fileService, logService); services.set(IUserDataProfilesService, userDataProfilesMainService); // Policy @@ -244,10 +250,7 @@ class CodeMain { ].map(path => path ? FSPromises.mkdir(path, { recursive: true }) : undefined)), // State service - stateMainService.init(), - - // User Data Profiles Service - userDataProfilesMainService.init(), + stateMainService.init().then(() => userDataProfilesMainService.init()), // Configuration service configurationService.initialize() diff --git a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts index 81bb26375b2..7da986b840b 100644 --- a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts +++ b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts @@ -242,7 +242,7 @@ export class SharedProcess extends Disposable implements ISharedProcess { appRoot: this.environmentMainService.appRoot, codeCachePath: this.environmentMainService.codeCachePath, backupWorkspacesPath: this.environmentMainService.backupWorkspacesPath, - profiles: this.userDataProfilesService.serialize(), + defaultProfile: this.userDataProfilesService.defaultProfile, userEnv: this.userEnv, args: this.environmentMainService.args, logLevel: this.logService.getLevel(), diff --git a/src/vs/platform/sharedProcess/node/sharedProcess.ts b/src/vs/platform/sharedProcess/node/sharedProcess.ts index b5656e2d080..e445bc596d0 100644 --- a/src/vs/platform/sharedProcess/node/sharedProcess.ts +++ b/src/vs/platform/sharedProcess/node/sharedProcess.ts @@ -7,8 +7,9 @@ import { IStringDictionary } from 'vs/base/common/collections'; import { ISandboxConfiguration } from 'vs/base/parts/sandbox/common/sandboxTypes'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { LogLevel } from 'vs/platform/log/common/log'; -import { IUserDataProfilesDto } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { PolicyDefinition, PolicyValue } from 'vs/platform/policy/common/policy'; +import { UriDto } from 'vs/base/common/types'; export interface ISharedProcess { @@ -28,7 +29,7 @@ export interface ISharedProcessConfiguration extends ISandboxConfiguration { readonly backupWorkspacesPath: string; - readonly profiles: IUserDataProfilesDto; + readonly defaultProfile: UriDto; readonly policiesData?: IStringDictionary<{ definition: PolicyDefinition; value: PolicyValue }>; } diff --git a/src/vs/platform/storage/common/storageIpc.ts b/src/vs/platform/storage/common/storageIpc.ts index e61a2736999..90572b900ea 100644 --- a/src/vs/platform/storage/common/storageIpc.ts +++ b/src/vs/platform/storage/common/storageIpc.ts @@ -5,9 +5,10 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; +import { UriDto } from 'vs/base/common/types'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/base/parts/storage/common/storage'; -import { IUserDataProfileDto, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { ISerializedSingleFolderWorkspaceIdentifier, ISerializedWorkspaceIdentifier, IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export type Key = string; @@ -21,7 +22,7 @@ export interface IBaseSerializableStorageRequest { * workspace is provided. Can be undefined to denote * application scope. */ - readonly profile: IUserDataProfileDto | undefined; + readonly profile: UriDto | undefined; /** * Workspace to correlate storage. Can be undefined to @@ -46,7 +47,7 @@ abstract class BaseStorageDatabaseClient extends Disposable implements IStorageD constructor( protected channel: IChannel, - protected profile: IUserDataProfileDto | undefined, + protected profile: UriDto | undefined, protected workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined ) { super(); @@ -81,7 +82,7 @@ abstract class BaseProfileAwareStorageDatabaseClient extends BaseStorageDatabase private readonly _onDidChangeItemsExternal = this._register(new Emitter()); readonly onDidChangeItemsExternal = this._onDidChangeItemsExternal.event; - constructor(channel: IChannel, profile: IUserDataProfileDto | undefined) { + constructor(channel: IChannel, profile: UriDto | undefined) { super(channel, profile, undefined); this.registerListeners(); @@ -119,7 +120,7 @@ class ApplicationStorageDatabaseClient extends BaseProfileAwareStorageDatabaseCl class GlobalStorageDatabaseClient extends BaseProfileAwareStorageDatabaseClient { - constructor(channel: IChannel, profile: IUserDataProfileDto) { + constructor(channel: IChannel, profile: UriDto) { super(channel, profile); } diff --git a/src/vs/platform/userDataProfile/common/userDataProfile.ts b/src/vs/platform/userDataProfile/common/userDataProfile.ts index b916e7510be..1d71b72f781 100644 --- a/src/vs/platform/userDataProfile/common/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/common/userDataProfile.ts @@ -3,17 +3,35 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { coalesce } from 'vs/base/common/arrays'; -import { Emitter, Event } from 'vs/base/common/event'; import { hash } from 'vs/base/common/hash'; import { Disposable } from 'vs/base/common/lifecycle'; import { joinPath } from 'vs/base/common/resources'; import { UriDto } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; +import { localize } from 'vs/nls'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files'; +import { IFileService } from 'vs/platform/files/common/files'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; +import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; + +export type ProfileOptions = { + settings?: boolean; + keybindings?: boolean; + tasks?: boolean; + snippets?: boolean; + extensions?: boolean; + uiState?: boolean; +}; + +export const DefaultOptions: ProfileOptions = { + settings: true, + keybindings: true, + tasks: true, + snippets: true, + extensions: true, + uiState: true +}; export interface IUserDataProfile { readonly id: string; @@ -28,30 +46,23 @@ export interface IUserDataProfile { readonly extensionsResource: URI | undefined; } -export type IUserDataProfileDto = UriDto; -export type IUserDataProfilesDto = { - readonly current: IUserDataProfileDto; - readonly default: IUserDataProfileDto; -}; - export const IUserDataProfilesService = createDecorator('IUserDataProfilesService'); export interface IUserDataProfilesService { readonly _serviceBrand: undefined; readonly profilesHome: URI; readonly defaultProfile: IUserDataProfile; - - readonly onDidChangeCurrentProfile: Event; readonly currentProfile: IUserDataProfile; - createProfile(name: string): IUserDataProfile; - setProfile(name: string): Promise; + newProfile(name: string, options?: ProfileOptions): IUserDataProfile; + createProfile(profile: IUserDataProfile, options: ProfileOptions, workspaceIdentifier?: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise; + setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise; + getProfile(workspace: URI): IUserDataProfile; getAllProfiles(): Promise; - - serialize(): IUserDataProfilesDto; + removeProfile(profile: IUserDataProfile): Promise; } -function reviveProfile(profile: IUserDataProfile, scheme: string): IUserDataProfile { +export function reviveProfile(profile: UriDto, scheme: string): IUserDataProfile { return { id: profile.id, isDefault: profile.isDefault, @@ -69,74 +80,49 @@ function reviveProfile(profile: IUserDataProfile, scheme: string): IUserDataProf export class UserDataProfilesService extends Disposable implements IUserDataProfilesService { readonly _serviceBrand: undefined; - protected static DEFAULT_PROFILE_NAME = 'default'; + readonly profilesHome: URI; protected _currentProfile: IUserDataProfile; get currentProfile(): IUserDataProfile { return this._currentProfile; } - readonly profilesHome: URI; protected _defaultProfile: IUserDataProfile; get defaultProfile(): IUserDataProfile { return this._defaultProfile; } - private readonly _onDidChangeCurrentProfile = this._register(new Emitter()); - readonly onDidChangeCurrentProfile = this._onDidChangeCurrentProfile.event; - constructor( - defaultProfile: IUserDataProfile | undefined, - currentProfile: IUserDataProfile | undefined, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + defaultProfile: UriDto | undefined, + currentProfile: UriDto | undefined, + @IEnvironmentService protected readonly environmentService: IEnvironmentService, @IFileService protected readonly fileService: IFileService, @ILogService protected readonly logService: ILogService ) { super(); this.profilesHome = joinPath(this.environmentService.userRoamingDataHome, 'profiles'); - this._defaultProfile = defaultProfile ? reviveProfile(defaultProfile, this.profilesHome.scheme) : this.createProfile(undefined); + this._defaultProfile = defaultProfile ? reviveProfile(defaultProfile, this.profilesHome.scheme) : this.toUserDataProfile(localize('defaultProfile', "Default"), environmentService.userRoamingDataHome, { ...DefaultOptions, extensions: false }, true); this._currentProfile = currentProfile ? reviveProfile(currentProfile, this.profilesHome.scheme) : this._defaultProfile; } - createProfile(name: string | undefined): IUserDataProfile { - const isDefault = !name || name === UserDataProfilesService.DEFAULT_PROFILE_NAME; - const location = name && name !== UserDataProfilesService.DEFAULT_PROFILE_NAME ? joinPath(this.profilesHome, name) : this.environmentService.userRoamingDataHome; + newProfile(name: string, options: ProfileOptions = DefaultOptions): IUserDataProfile { + return this.toUserDataProfile(name, joinPath(this.profilesHome, hash(name).toString(16)), options, this.defaultProfile); + } + + protected toUserDataProfile(name: string, location: URI, options: ProfileOptions, defaultProfile: true | IUserDataProfile): IUserDataProfile { return { id: hash(location.toString()).toString(16), - isDefault, - name: name ?? UserDataProfilesService.DEFAULT_PROFILE_NAME, - location, - globalStorageHome: joinPath(location, 'globalStorage'), - settingsResource: joinPath(location, 'settings.json'), - keybindingsResource: joinPath(location, 'keybindings.json'), - tasksResource: joinPath(location, 'tasks.json'), - snippetsHome: joinPath(location, 'snippets'), - extensionsResource: name ? joinPath(location, 'extensions.json') : undefined + name: name, + location: location, + isDefault: defaultProfile === true, + globalStorageHome: defaultProfile === true || options.uiState ? joinPath(location, 'globalStorage') : defaultProfile.globalStorageHome, + settingsResource: defaultProfile === true || options.settings ? joinPath(location, 'settings.json') : defaultProfile.settingsResource, + keybindingsResource: defaultProfile === true || options.keybindings ? joinPath(location, 'keybindings.json') : defaultProfile.keybindingsResource, + tasksResource: defaultProfile === true || options.tasks ? joinPath(location, 'tasks.json') : defaultProfile.tasksResource, + snippetsHome: defaultProfile === true || options.snippets ? joinPath(location, 'snippets') : defaultProfile.snippetsHome, + extensionsResource: defaultProfile === true && !options.extensions ? undefined : joinPath(location, 'extensions.json'), }; } - async getAllProfiles(): Promise { - try { - const stat = await this.fileService.resolve(this.profilesHome); - const profiles = coalesce(stat.children?.map(stat => stat.isDirectory ? this.createProfile(stat.name) : undefined) ?? []); - if (profiles.length) { - profiles.unshift(this._defaultProfile); - } - return profiles; - } catch (error) { - if ((error).fileOperationResult !== FileOperationResult.FILE_NOT_FOUND) { - this.logService.error('Error while getting all profiles', error); - } - } - return []; - } - - protected createCurrentProfile(profile: string | undefined): IUserDataProfile { - return profile === UserDataProfilesService.DEFAULT_PROFILE_NAME ? this._defaultProfile : this.createProfile(profile); - } - - setProfile(name: string): Promise { throw new Error('Not implemented'); } - - serialize(): IUserDataProfilesDto { - return { - default: this.defaultProfile, - current: this.currentProfile - }; - } + getAllProfiles(): Promise { throw new Error('Not implemented'); } + createProfile(profile: IUserDataProfile, options: ProfileOptions, workspaceIdentifier?: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise { throw new Error('Not implemented'); } + setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise { throw new Error('Not implemented'); } + getProfile(workspace: URI): IUserDataProfile { throw new Error('Not implemented'); } + removeProfile(profile: IUserDataProfile): Promise { throw new Error('Not implemented'); } } diff --git a/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts b/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts index 1a13319dc3d..8c83a01a59d 100644 --- a/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts @@ -3,18 +3,42 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { ResourceMap } from 'vs/base/common/map'; +import { revive } from 'vs/base/common/marshalling'; +import { UriDto } from 'vs/base/common/types'; +import { URI } from 'vs/base/common/uri'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; import { IStateMainService } from 'vs/platform/state/electron-main/state'; -import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { ProfileOptions, DefaultOptions, IUserDataProfile, IUserDataProfilesService, UserDataProfilesService, reviveProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; + +type UserDataProfiles = { + profiles: IUserDataProfile[]; + workspaces: ResourceMap; +}; + +type StoredUserDataProfile = { + name: string; + location: URI; + options: ProfileOptions; +}; + +type StoredWorkspaceInfo = { + workspace: URI; + profile: URI; +}; export class UserDataProfilesMainService extends UserDataProfilesService implements IUserDataProfilesService { - private static CURRENT_PROFILE_KEY = 'currentUserDataProfile'; + private static readonly PROFILES_KEY = 'userDataProfiles'; + private static readonly WORKSPACE_PROFILE_INFO_KEY = 'workspaceAndProfileInfo'; constructor( @IStateMainService private readonly stateMainService: IStateMainService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, @IEnvironmentService environmentService: IEnvironmentService, @IFileService fileService: IFileService, @ILogService logService: ILogService, @@ -22,23 +46,90 @@ export class UserDataProfilesMainService extends UserDataProfilesService impleme super(undefined, undefined, environmentService, fileService, logService); } - async init(): Promise { - const profileName = this.stateMainService.getItem(UserDataProfilesMainService.CURRENT_PROFILE_KEY); - if (profileName) { - const profiles = await this.getAllProfiles(); - const profile = profiles.find(p => p.name === profileName); - if (profile || (profileName === UserDataProfilesMainService.DEFAULT_PROFILE_NAME && profiles.length > 1)) { - this._defaultProfile = this.createProfile(UserDataProfilesService.DEFAULT_PROFILE_NAME); - this._currentProfile = profileName === UserDataProfilesMainService.DEFAULT_PROFILE_NAME ? this._defaultProfile : profile ?? this._defaultProfile; - } else { - this.stateMainService?.removeItem(UserDataProfilesMainService.CURRENT_PROFILE_KEY); - } + init(): void { + if (this.storedProfiles.length) { + this._defaultProfile = this.toUserDataProfile(this.defaultProfile.name, this.defaultProfile.location, DefaultOptions, true); } } - override async setProfile(name: string): Promise { - this.stateMainService?.setItem(UserDataProfilesMainService.CURRENT_PROFILE_KEY, name); + private _profiles: UserDataProfiles | undefined; + private get profiles(): UserDataProfiles { + if (!this._profiles) { + const profiles = this.storedProfiles.map(storedProfile => this.toUserDataProfile(storedProfile.name, storedProfile.location, storedProfile.options, this.defaultProfile)); + profiles.unshift(this.defaultProfile); + const workspaces = this.storedWorskpaceInfos.reduce((workspaces, workspaceProfileInfo) => { + const profile = profiles.find(p => this.uriIdentityService.extUri.isEqual(p.location, workspaceProfileInfo.profile)); + if (profile) { + workspaces.set(workspaceProfileInfo.workspace, profile); + } + return workspaces; + }, new ResourceMap()); + this._profiles = { profiles: profiles, workspaces: workspaces }; + } + return this._profiles; + } + + override async getAllProfiles(): Promise { + return this.profiles.profiles; + } + + override getProfile(workspace: URI): IUserDataProfile { + return this.profiles.workspaces.get(workspace) ?? this.defaultProfile; + } + + override async createProfile(profile: IUserDataProfile, options: ProfileOptions, workspaceIdentifier?: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise { + profile = reviveProfile(profile, this.profilesHome.scheme); + if (this.storedProfiles.some(p => p.name === profile.name)) { + throw new Error(`Profile with name ${profile.name} already exists`); + } + const storedProfile: StoredUserDataProfile = { name: profile.name, location: profile.location, options }; + const storedProfiles = [...this.storedProfiles, storedProfile]; + this.storedProfiles = storedProfiles; + if (workspaceIdentifier) { + await this.setProfileForWorkspace(profile, workspaceIdentifier); + } + return this.profiles.profiles.find(p => this.uriIdentityService.extUri.isEqual(p.location, profile.location))!; + } + + override async setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise { + profile = reviveProfile(profile, this.profilesHome.scheme); + const workspace = isSingleFolderWorkspaceIdentifier(workspaceIdentifier) ? workspaceIdentifier.uri : workspaceIdentifier.configPath; + const storedWorkspaceInfos = this.storedWorskpaceInfos.filter(info => !this.uriIdentityService.extUri.isEqual(info.workspace, workspace)); + if (!profile.isDefault) { + storedWorkspaceInfos.push({ workspace, profile: profile.location }); + } + this.storedWorskpaceInfos = storedWorkspaceInfos; + return this.profiles.profiles.find(p => this.uriIdentityService.extUri.isEqual(p.location, profile.location))!; + } + + override async removeProfile(profile: IUserDataProfile): Promise { + if (profile.isDefault) { + throw new Error('Cannot remove default profile'); + } + profile = reviveProfile(profile, this.profilesHome.scheme); + if (!this.storedProfiles.some(p => this.uriIdentityService.extUri.isEqual(p.location, profile.location))) { + throw new Error(`Profile with name ${profile.name} does not exist`); + } + this.storedWorskpaceInfos = this.storedWorskpaceInfos.filter(p => !this.uriIdentityService.extUri.isEqual(p.profile, profile.location)); + this.storedProfiles = this.storedProfiles.filter(p => !this.uriIdentityService.extUri.isEqual(p.location, profile.location)); + } + + private get storedProfiles(): StoredUserDataProfile[] { + return revive(this.stateMainService.getItem[]>(UserDataProfilesMainService.PROFILES_KEY, [])); + } + + private set storedProfiles(storedProfiles: StoredUserDataProfile[]) { + this.stateMainService.setItem(UserDataProfilesMainService.PROFILES_KEY, storedProfiles); + this._profiles = undefined; + } + + private get storedWorskpaceInfos(): StoredWorkspaceInfo[] { + return revive(this.stateMainService.getItem[]>(UserDataProfilesMainService.WORKSPACE_PROFILE_INFO_KEY, [])); + } + + private set storedWorskpaceInfos(storedWorkspaceInfos: StoredWorkspaceInfo[]) { + this.stateMainService.setItem(UserDataProfilesMainService.WORKSPACE_PROFILE_INFO_KEY, storedWorkspaceInfos); + this._profiles = undefined; } } - diff --git a/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts b/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts index c28b57fe965..d2bcfd82043 100644 --- a/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts @@ -3,11 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { UriDto } from 'vs/base/common/types'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; -import { IUserDataProfile, IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { ProfileOptions, IUserDataProfile, IUserDataProfilesService, reviveProfile, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export class UserDataProfilesNativeService extends UserDataProfilesService implements IUserDataProfilesService { @@ -22,8 +24,23 @@ export class UserDataProfilesNativeService extends UserDataProfilesService imple super(defaultProfile, currentProfile, environmentService, fileService, logService); } - override setProfile(name: string): Promise { - return this.channel.call('setProfile', [name]); + override async createProfile(profile: IUserDataProfile, options: ProfileOptions, workspaceIdentifier?: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise { + const result = await this.channel.call>('createProfile', [profile, options, workspaceIdentifier]); + return reviveProfile(result, this.profilesHome.scheme); + } + + override async setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise { + const result = await this.channel.call>('setProfileForWorkspace', [profile, workspaceIdentifier]); + return reviveProfile(result, this.profilesHome.scheme); + } + + override async getAllProfiles(): Promise { + const result = await this.channel.call[]>('getAllProfiles'); + return result.map(profile => reviveProfile(profile, this.profilesHome.scheme)); + } + + override removeProfile(profile: IUserDataProfile): Promise { + return this.channel.call('removeProfile', [profile]); } } diff --git a/src/vs/platform/window/common/window.ts b/src/vs/platform/window/common/window.ts index c675d3e5b13..875bd45f761 100644 --- a/src/vs/platform/window/common/window.ts +++ b/src/vs/platform/window/common/window.ts @@ -6,6 +6,7 @@ import { IStringDictionary } from 'vs/base/common/collections'; import { PerformanceMark } from 'vs/base/common/performance'; import { isLinux, isMacintosh, isNative, isWeb, isWindows } from 'vs/base/common/platform'; +import { UriDto } from 'vs/base/common/types'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ISandboxConfiguration } from 'vs/base/parts/sandbox/common/sandboxTypes'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -16,7 +17,7 @@ import { FileType } from 'vs/platform/files/common/files'; import { LogLevel } from 'vs/platform/log/common/log'; import { PolicyDefinition, PolicyValue } from 'vs/platform/policy/common/policy'; import { IPartsSplash } from 'vs/platform/theme/common/themeService'; -import { IUserDataProfilesDto } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export const WindowMinimumSize = { @@ -283,7 +284,10 @@ export interface INativeWindowConfiguration extends IWindowConfiguration, Native execPath: string; backupPath?: string; - profiles: IUserDataProfilesDto; + profiles: { + default: UriDto; + current: UriDto; + }; homeDir: string; tmpDir: string; diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index 8ec5afe488f..423cb251878 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -40,7 +40,7 @@ import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electro import { IWindowState, ICodeWindow, ILoadEvent, WindowMode, WindowError, LoadReason, defaultWindowState } from 'vs/platform/window/electron-main/window'; import { Color } from 'vs/base/common/color'; import { IPolicyService } from 'vs/platform/policy/common/policy'; -import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { revive } from 'vs/base/common/marshalling'; export interface IWindowCreationOptions { @@ -156,6 +156,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { @ILogService private readonly logService: ILogService, @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @IPolicyService private readonly policyService: IPolicyService, + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @IFileService private readonly fileService: IFileService, @IApplicationStorageMainService private readonly applicationStorageMainService: IApplicationStorageMainService, @IStorageMainService private readonly storageMainService: IStorageMainService, @@ -882,6 +883,10 @@ export class CodeWindow extends Disposable implements ICodeWindow { configuration.isInitialStartup = false; // since this is a reload configuration.policiesData = this.policyService.serialize(); // set policies data again + configuration.profiles = { + default: this.userDataProfilesService.defaultProfile, + current: configuration.workspace ? this.userDataProfilesService.getProfile(isWorkspaceIdentifier(configuration.workspace) ? configuration.workspace.configPath : configuration.workspace.uri) : this.userDataProfilesService.defaultProfile, + }; // Load config this.load(configuration, { isReload: true, disableExtensions: cli?.['disable-extensions'] }); diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 43375f06971..994c4002165 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -1300,7 +1300,10 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // loading the window. backupPath: options.emptyWindowBackupInfo ? join(this.environmentMainService.backupHome, options.emptyWindowBackupInfo.backupFolder) : undefined, - profiles: this.userDataProfilesService.serialize(), + profiles: { + default: this.userDataProfilesService.defaultProfile, + current: options.workspace ? this.userDataProfilesService.getProfile(isWorkspaceIdentifier(options.workspace) ? options.workspace.configPath : options.workspace.uri) : this.userDataProfilesService.defaultProfile, + }, homeDir: this.environmentMainService.userHome.fsPath, tmpDir: this.environmentMainService.tmpDir.fsPath, diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.contribution.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.contribution.ts index 023f1495974..e4e9946c3c8 100644 --- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.contribution.ts +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.contribution.ts @@ -3,28 +3,33 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Disposable } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar'; import { PROFILES_CATEGORY } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import '../common/profileActions'; -// import '../common/userDataProfileActions'; +import '../common/userDataProfileActions'; -class UserDataProfileStatusBarEntryContribution implements IWorkbenchContribution { +class UserDataProfileStatusBarEntryContribution extends Disposable implements IWorkbenchContribution { constructor( @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, - @IStatusbarService private readonly statusBarService: IStatusbarService + @IStatusbarService private readonly statusBarService: IStatusbarService, + @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, ) { + super(); this.updateStatus(); + this._register(this.workspaceContextService.onDidChangeWorkbenchState(() => this.updateStatus())); } private async updateStatus(): Promise { const profiles = await this.userDataProfilesService.getAllProfiles(); - if (profiles.length) { + if (profiles.length > 1 && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY) { this.statusBarService.addEntry({ name: this.userDataProfilesService.currentProfile.name!, command: 'workbench.profiles.actions.switchProfile', diff --git a/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts b/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts index ea2911b9457..1c6f6b1ecf0 100644 --- a/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts +++ b/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts @@ -17,6 +17,11 @@ import { asJson, asText, IRequestService } from 'vs/platform/request/common/requ import { IUserDataProfileTemplate, isProfile, IUserDataProfileManagementService, IUserDataProfileWorkbenchService, PROFILES_CATEGORY, PROFILE_EXTENSION, PROFILE_FILTER } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IsDevelopmentContext } from 'vs/platform/contextkey/common/contextkeys'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { WorkbenchStateContext } from 'vs/workbench/common/contextkeys'; +import { CATEGORIES } from 'vs/workbench/common/actions'; +import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; registerAction2(class SaveProfileAsAction extends Action2 { constructor() { @@ -27,7 +32,8 @@ registerAction2(class SaveProfileAsAction extends Action2 { original: 'Save Settings Profile As...' }, category: PROFILES_CATEGORY, - f1: true + f1: true, + precondition: ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')), }); } @@ -39,7 +45,7 @@ registerAction2(class SaveProfileAsAction extends Action2 { title: localize('save profile as', "Save Settings Profile As..."), }); if (name) { - await userDataProfileManagementService.createAndEnterProfile(name); + await userDataProfileManagementService.createAndEnterProfile(name, undefined, true); } } }); @@ -53,7 +59,8 @@ registerAction2(class SwitchProfileAction extends Action2 { original: 'Switch Settings Profile' }, category: PROFILES_CATEGORY, - f1: true + f1: true, + precondition: ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')), }); } @@ -85,7 +92,8 @@ registerAction2(class RemoveProfileAction extends Action2 { original: 'Remove Settings Profile' }, category: PROFILES_CATEGORY, - f1: true + f1: true, + precondition: ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')), }); } @@ -104,16 +112,70 @@ registerAction2(class RemoveProfileAction extends Action2 { } }); +registerAction2(class CleanupProfilesAction extends Action2 { + constructor() { + super({ + id: 'workbench.profiles.actions.cleanupProfiles', + title: { + value: localize('cleanup profile', "Cleanup Profiles"), + original: 'Cleanup Profiles' + }, + category: CATEGORIES.Developer, + f1: true, + precondition: IsDevelopmentContext, + }); + } + + async run(accessor: ServicesAccessor) { + const userDataProfilesService = accessor.get(IUserDataProfilesService); + const fileService = accessor.get(IFileService); + const uriIdentityService = accessor.get(IUriIdentityService); + + const allProfiles = await userDataProfilesService.getAllProfiles(); + const stat = await fileService.resolve(userDataProfilesService.profilesHome); + await Promise.all((stat.children || [])?.filter(child => child.isDirectory && allProfiles.every(p => !uriIdentityService.extUri.isEqual(p.location, child.resource))) + .map(child => fileService.del(child.resource, { recursive: true }))); + } +}); + +registerAction2(class CreateAndEnterEmptyProfileAction extends Action2 { + constructor() { + super({ + id: 'workbench.profiles.actions.createAndEnterEmptyProfile', + title: { + value: localize('create and enter empty profile', "Create and Enter Empty Profile..."), + original: 'Create and Enter Empty Profile...' + }, + category: PROFILES_CATEGORY, + f1: true, + precondition: ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')), + }); + } + + async run(accessor: ServicesAccessor) { + const quickInputService = accessor.get(IQuickInputService); + const userDataProfileManagementService = accessor.get(IUserDataProfileManagementService); + const name = await quickInputService.input({ + placeHolder: localize('name', "Profile name"), + title: localize('create and enter empty profile', "Create and Enter Empty Profile..."), + }); + if (name) { + await userDataProfileManagementService.createAndEnterProfile(name); + } + } +}); + registerAction2(class ExportProfileAction extends Action2 { constructor() { super({ - id: 'workbench.profiles.actions.exportProfile', + id: 'workbench.profiles.actions.exportProfile2', title: { - value: localize('export profile', "Export Settings Profile as a Template..."), - original: 'Export Settings as a Profile as a Template...' + value: localize('export profile', "Export Settings as a Profile (2)..."), + original: 'Export Settings as a Profile as a Profile (2)...' }, category: PROFILES_CATEGORY, - f1: true + f1: true, + precondition: ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')), }); } @@ -140,16 +202,17 @@ registerAction2(class ExportProfileAction extends Action2 { } }); -registerAction2(class CreateProfileFromTemplateAction extends Action2 { +registerAction2(class ImportProfileAction extends Action2 { constructor() { super({ - id: 'workbench.profiles.actions.createProfileFromTemplate', + id: 'workbench.profiles.actions.importProfile2', title: { - value: localize('create profile from template', "Create Settings Profile from Template..."), - original: 'Create Settings Profile from Template...' + value: localize('import profile', "Import Settings from a Profile (2)..."), + original: 'Import Settings from a Profile (2)...' }, category: PROFILES_CATEGORY, - f1: true + f1: true, + precondition: ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')), }); } diff --git a/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts b/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts index 27336f47e71..83afead166e 100644 --- a/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts +++ b/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts @@ -17,6 +17,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { ISingleFolderWorkspaceIdentifier, IWorkspaceContextService, IWorkspaceIdentifier, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IExtensionManagementServerService, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { CreationOptions, IUserDataProfileManagementService, IUserDataProfileTemplate, PROFILES_CATEGORY } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; @@ -42,38 +43,48 @@ export class UserDataProfileManagementService extends Disposable implements IUse @IHostService private readonly hostService: IHostService, @IDialogService private readonly dialogService: IDialogService, @IProgressService private readonly progressService: IProgressService, + @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @ILogService logService: ILogService ) { super(); } - async createAndEnterProfile(name: string, options: CreationOptions = DefaultOptions): Promise { + async createAndEnterProfile(name: string, options: CreationOptions = DefaultOptions, fromExisting?: boolean): Promise { + const workspaceIdentifier = this.getWorkspaceIdentifier(); + if (!workspaceIdentifier) { + throw new Error(localize('cannotCreateProfileInEmptyWorkbench', "Cannot create a profile in an empty workspace")); + } const promises: Promise[] = []; - const newProfile = this.userDataProfilesService.createProfile(name); + const newProfile = this.userDataProfilesService.newProfile(name); await this.fileService.createFolder(newProfile.location); - if (options?.uiState) { - promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.globalStorageHome, newProfile.globalStorageHome)); - } - if (options?.settings) { - promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.settingsResource, newProfile.settingsResource)); - } - if (options?.extensions && newProfile.extensionsResource) { - promises.push((async () => { - const extensionsProfileResource = this.userDataProfilesService.currentProfile.extensionsResource ?? await this.createDefaultExtensionsProfile(joinPath(this.userDataProfilesService.defaultProfile.location, basename(newProfile.extensionsResource!))); - this.fileService.copy(extensionsProfileResource, newProfile.extensionsResource!); - })()); - } - if (options?.keybindings) { - promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.keybindingsResource, newProfile.keybindingsResource)); - } - if (options?.tasks) { - promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.tasksResource, newProfile.tasksResource)); - } - if (options?.snippets) { - promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.snippetsHome, newProfile.snippetsHome)); + if (fromExisting) { + if (options?.uiState) { + promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.globalStorageHome, newProfile.globalStorageHome)); + } + if (options?.settings) { + promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.settingsResource, newProfile.settingsResource)); + } + if (options?.extensions && newProfile.extensionsResource) { + promises.push((async () => { + const extensionsProfileResource = this.userDataProfilesService.currentProfile.extensionsResource ?? await this.createDefaultExtensionsProfile(joinPath(this.userDataProfilesService.defaultProfile.location, basename(newProfile.extensionsResource!))); + this.fileService.copy(extensionsProfileResource, newProfile.extensionsResource!); + })()); + } + if (options?.keybindings) { + promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.keybindingsResource, newProfile.keybindingsResource)); + } + if (options?.tasks) { + promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.tasksResource, newProfile.tasksResource)); + } + if (options?.snippets) { + promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.snippetsHome, newProfile.snippetsHome)); + } + } else { + promises.push(this.fileService.createFolder(newProfile.globalStorageHome)); } await Promise.allSettled(promises); - await this.doSwitchProfile(name); + await this.userDataProfilesService.createProfile(newProfile, options, workspaceIdentifier); + await this.enterProfile(); } async removeProfile(name: string): Promise { @@ -88,6 +99,7 @@ export class UserDataProfileManagementService extends Disposable implements IUse if (!profile) { throw new Error(`Profile ${name} does not exist`); } + await this.userDataProfilesService.removeProfile(profile); if (profiles.length === 2) { await this.fileService.del(this.userDataProfilesService.profilesHome, { recursive: true }); } else { @@ -96,21 +108,30 @@ export class UserDataProfileManagementService extends Disposable implements IUse } async switchProfile(name: string): Promise { + const workspaceIdentifier = this.getWorkspaceIdentifier(); + if (!workspaceIdentifier) { + throw new Error(localize('cannotSwitchProfileInEmptyWorkbench', "Cannot switch a profile in an empty workspace")); + } const profiles = await this.userDataProfilesService.getAllProfiles(); const profile = profiles.find(p => p.name === name); if (!profile) { throw new Error(`Profile ${name} does not exist`); } - await this.doSwitchProfile(name); + await this.userDataProfilesService.setProfileForWorkspace(profile, workspaceIdentifier); + await this.enterProfile(); } async createAndEnterProfileFromTemplate(name: string, template: IUserDataProfileTemplate, options: CreationOptions = DefaultOptions): Promise { + const workspaceIdentifier = this.getWorkspaceIdentifier(); + if (!workspaceIdentifier) { + throw new Error(localize('cannotCreateProfileInEmptyWorkbench', "Cannot create a profile in an empty workspace")); + } await this.progressService.withProgress({ location: ProgressLocation.Notification, title: localize('profiles.creating', "{0}: Creating...", PROFILES_CATEGORY), }, async progress => { const promises: Promise[] = []; - const newProfile = this.userDataProfilesService.createProfile(name); + const newProfile = this.userDataProfilesService.newProfile(name); await this.fileService.createFolder(newProfile.location); if (template.globalState) { // todo: create global state @@ -122,26 +143,30 @@ export class UserDataProfileManagementService extends Disposable implements IUse promises.push(this.fileService.writeFile(newProfile.extensionsResource, VSBuffer.fromString(template.extensions))); } await Promise.allSettled(promises); + await this.userDataProfilesService.createProfile(newProfile, options, workspaceIdentifier); }); - await this.doSwitchProfile(name); + await this.enterProfile(); } - async reset(): Promise { - if (this.userDataProfilesService.currentProfile.name !== this.userDataProfilesService.defaultProfile.name) { - throw new Error('Please switch to default profile to reset'); + private getWorkspaceIdentifier(): ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier | undefined { + const workspace = this.workspaceContextService.getWorkspace(); + switch (this.workspaceContextService.getWorkbenchState()) { + case WorkbenchState.FOLDER: + return { uri: workspace.folders[0].uri, id: workspace.id }; + case WorkbenchState.WORKSPACE: + return { configPath: workspace.configuration!, id: workspace.id }; } - await this.fileService.del(this.userDataProfilesService.profilesHome); + return undefined; } - private async doSwitchProfile(name: string): Promise { - await this.userDataProfilesService.setProfile(name); + private async enterProfile(): Promise { const result = await this.dialogService.confirm({ type: 'info', - message: localize('restart message', "Switching a profile requires restarting VS Code."), - primaryButton: localize('restart button', "&&Restart"), + message: localize('reload message', "Switching a profile requires reloading VS Code."), + primaryButton: localize('reload button', "&&Reload"), }); if (result.confirmed) { - await this.hostService.restart(); + await this.hostService.reload(); } } diff --git a/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts b/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts index 3d43ca85d1f..a63d81994c0 100644 --- a/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts +++ b/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts @@ -20,12 +20,11 @@ export const IUserDataProfileManagementService = createDecorator; + createAndEnterProfile(name: string, options?: CreationOptions, fromExisting?: boolean): Promise; createAndEnterProfileFromTemplate(name: string, template: IUserDataProfileTemplate, options?: CreationOptions): Promise; removeProfile(name: string): Promise; switchProfile(name: string): Promise; - reset(): Promise; } export interface IUserDataProfileTemplate { From 8e2ec5a7ee1ae5500c645c05145359f2a814611c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Wed, 15 Jun 2022 09:56:48 +0200 Subject: [PATCH 029/175] Revert "remove UpdateMode policy (#150357)" (#152155) This reverts commit 73dda0c06add139307f6be753cea50028bcde05b. --- build/azure-pipelines/win32/product-build-win32.yml | 7 +++++++ build/gulpfile.vscode.js | 3 +++ .../platform/update/common/update.config.contribution.ts | 6 +++++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 7feb8af4e98..65504f03ecd 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -127,6 +127,13 @@ steps: exec { node build/azure-pipelines/mixin } displayName: Mix in quality + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { node build\lib\policies } + displayName: Generate Group Policy definitions + condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) + - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 3315caf4bff..4a47b63c823 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -331,6 +331,9 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op result = es.merge(result, gulp.src('resources/win32/VisualElementsManifest.xml', { base: 'resources/win32' }) .pipe(rename(product.nameShort + '.VisualElementsManifest.xml'))); + result = es.merge(result, gulp.src('.build/policies/win32/**', { base: '.build/policies/win32' }) + .pipe(rename(f => f.dirname = `policies/${f.dirname}`))); + } else if (platform === 'linux') { result = es.merge(result, gulp.src('resources/linux/bin/code.sh', { base: '.' }) .pipe(replace('@@PRODNAME@@', product.nameLong)) diff --git a/src/vs/platform/update/common/update.config.contribution.ts b/src/vs/platform/update/common/update.config.contribution.ts index 5ceb95727b5..4134233def6 100644 --- a/src/vs/platform/update/common/update.config.contribution.ts +++ b/src/vs/platform/update/common/update.config.contribution.ts @@ -27,7 +27,11 @@ configurationRegistry.registerConfiguration({ localize('manual', "Disable automatic background update checks. Updates will be available if you manually check for updates."), localize('start', "Check for updates only on startup. Disable automatic background update checks."), localize('default', "Enable automatic update checks. Code will check for updates automatically and periodically.") - ] + ], + policy: { + name: 'UpdateMode', + minimumVersion: '1.67', + } }, 'update.channel': { type: 'string', From 83326654e383b100cc6cc566ec0276690e4b501c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 15 Jun 2022 14:00:59 +0200 Subject: [PATCH 030/175] use workspaceIdentifier while getting profile (#152177) --- .../platform/userDataProfile/common/userDataProfile.ts | 4 ++-- .../userDataProfile/electron-main/userDataProfile.ts | 10 +++++++--- src/vs/platform/windows/electron-main/window.ts | 2 +- .../windows/electron-main/windowsMainService.ts | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/vs/platform/userDataProfile/common/userDataProfile.ts b/src/vs/platform/userDataProfile/common/userDataProfile.ts index 1d71b72f781..c1e32808e7f 100644 --- a/src/vs/platform/userDataProfile/common/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/common/userDataProfile.ts @@ -57,7 +57,7 @@ export interface IUserDataProfilesService { newProfile(name: string, options?: ProfileOptions): IUserDataProfile; createProfile(profile: IUserDataProfile, options: ProfileOptions, workspaceIdentifier?: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise; setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise; - getProfile(workspace: URI): IUserDataProfile; + getProfile(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): IUserDataProfile; getAllProfiles(): Promise; removeProfile(profile: IUserDataProfile): Promise; } @@ -123,6 +123,6 @@ export class UserDataProfilesService extends Disposable implements IUserDataProf getAllProfiles(): Promise { throw new Error('Not implemented'); } createProfile(profile: IUserDataProfile, options: ProfileOptions, workspaceIdentifier?: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise { throw new Error('Not implemented'); } setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise { throw new Error('Not implemented'); } - getProfile(workspace: URI): IUserDataProfile { throw new Error('Not implemented'); } + getProfile(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): IUserDataProfile { throw new Error('Not implemented'); } removeProfile(profile: IUserDataProfile): Promise { throw new Error('Not implemented'); } } diff --git a/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts b/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts index 8c83a01a59d..3340b69c350 100644 --- a/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts @@ -73,8 +73,8 @@ export class UserDataProfilesMainService extends UserDataProfilesService impleme return this.profiles.profiles; } - override getProfile(workspace: URI): IUserDataProfile { - return this.profiles.workspaces.get(workspace) ?? this.defaultProfile; + override getProfile(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): IUserDataProfile { + return this.profiles.workspaces.get(this.getWorkspace(workspaceIdentifier)) ?? this.defaultProfile; } override async createProfile(profile: IUserDataProfile, options: ProfileOptions, workspaceIdentifier?: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise { @@ -93,7 +93,7 @@ export class UserDataProfilesMainService extends UserDataProfilesService impleme override async setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise { profile = reviveProfile(profile, this.profilesHome.scheme); - const workspace = isSingleFolderWorkspaceIdentifier(workspaceIdentifier) ? workspaceIdentifier.uri : workspaceIdentifier.configPath; + const workspace = this.getWorkspace(workspaceIdentifier); const storedWorkspaceInfos = this.storedWorskpaceInfos.filter(info => !this.uriIdentityService.extUri.isEqual(info.workspace, workspace)); if (!profile.isDefault) { storedWorkspaceInfos.push({ workspace, profile: profile.location }); @@ -102,6 +102,10 @@ export class UserDataProfilesMainService extends UserDataProfilesService impleme return this.profiles.profiles.find(p => this.uriIdentityService.extUri.isEqual(p.location, profile.location))!; } + private getWorkspace(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier) { + return isSingleFolderWorkspaceIdentifier(workspaceIdentifier) ? workspaceIdentifier.uri : workspaceIdentifier.configPath; + } + override async removeProfile(profile: IUserDataProfile): Promise { if (profile.isDefault) { throw new Error('Cannot remove default profile'); diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index 423cb251878..6220ad7c1a4 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -885,7 +885,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { configuration.policiesData = this.policyService.serialize(); // set policies data again configuration.profiles = { default: this.userDataProfilesService.defaultProfile, - current: configuration.workspace ? this.userDataProfilesService.getProfile(isWorkspaceIdentifier(configuration.workspace) ? configuration.workspace.configPath : configuration.workspace.uri) : this.userDataProfilesService.defaultProfile, + current: configuration.workspace ? this.userDataProfilesService.getProfile(configuration.workspace) : this.userDataProfilesService.defaultProfile, }; // Load config diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 994c4002165..4f7156d8d33 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -1302,7 +1302,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic profiles: { default: this.userDataProfilesService.defaultProfile, - current: options.workspace ? this.userDataProfilesService.getProfile(isWorkspaceIdentifier(options.workspace) ? options.workspace.configPath : options.workspace.uri) : this.userDataProfilesService.defaultProfile, + current: options.workspace ? this.userDataProfilesService.getProfile(options.workspace) : this.userDataProfilesService.defaultProfile, }, homeDir: this.environmentMainService.userHome.fsPath, From 132a7e6396250b13d795e24859cadcffd9604f1e Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Wed, 15 Jun 2022 14:29:33 +0200 Subject: [PATCH 031/175] Dispose overview rulers when the diff editor model is set (#152182) Fixes #152176: Dispose overview rulers when the diff editor model is set --- .../editor/browser/widget/diffEditorWidget.ts | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index ab0ec218a28..209299b7a71 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -6,6 +6,7 @@ import 'vs/css!./media/diffEditor'; import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; +import * as assert from 'vs/base/common/assert'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { ISashEvent, IVerticalSashLayoutProvider, Sash, SashState, Orientation } from 'vs/base/browser/ui/sash/sash'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -429,24 +430,30 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE return result; } - private _recreateOverviewRulers(): void { + private _disposeOverviewRulers(): void { + if (this._originalOverviewRuler) { + this._overviewDomElement.removeChild(this._originalOverviewRuler.getDomNode()); + this._originalOverviewRuler.dispose(); + this._originalOverviewRuler = null; + } + if (this._modifiedOverviewRuler) { + this._overviewDomElement.removeChild(this._modifiedOverviewRuler.getDomNode()); + this._modifiedOverviewRuler.dispose(); + this._modifiedOverviewRuler = null; + } + } + + private _createOverviewRulers(): void { if (!this._options.renderOverviewRuler) { return; } - if (this._originalOverviewRuler) { - this._overviewDomElement.removeChild(this._originalOverviewRuler.getDomNode()); - this._originalOverviewRuler.dispose(); - } + assert.ok(!this._originalOverviewRuler && !this._modifiedOverviewRuler); + if (this._originalEditor.hasModel()) { this._originalOverviewRuler = this._originalEditor.createOverviewRuler('original diffOverviewRuler')!; this._overviewDomElement.appendChild(this._originalOverviewRuler.getDomNode()); } - - if (this._modifiedOverviewRuler) { - this._overviewDomElement.removeChild(this._modifiedOverviewRuler.getDomNode()); - this._modifiedOverviewRuler.dispose(); - } if (this._modifiedEditor.hasModel()) { this._modifiedOverviewRuler = this._modifiedEditor.createOverviewRuler('modified diffOverviewRuler')!; this._overviewDomElement.appendChild(this._modifiedOverviewRuler.getDomNode()); @@ -794,6 +801,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE // Remove all view zones & decorations this._cleanViewZonesAndDecorations(); + this._disposeOverviewRulers(); + // Update code editor models this._originalEditor.setModel(model ? model.original : null); this._modifiedEditor.setModel(model ? model.modified : null); @@ -812,7 +821,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._setState(editorBrowser.DiffEditorState.Idle); if (model) { - this._recreateOverviewRulers(); + this._createOverviewRulers(); // Begin comparing this._beginUpdateDecorations(); From 68433fb7fdd6f16c1f86ef2e88e90dd070785b43 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 15 Jun 2022 14:34:56 +0200 Subject: [PATCH 032/175] tests - remote invalid proposed API (#152181) --- extensions/vscode-api-tests/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 7d1b1daca43..610dd2590e7 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -18,7 +18,6 @@ "fileSearchProvider", "findTextInFiles", "fsChunks", - "inlineCompletions", "notebookCellExecutionState", "notebookContentProvider", "notebookControllerKind", From 4f66d556f2429600fbea2376709a6d789c7df323 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 15 Jun 2022 05:48:32 -0700 Subject: [PATCH 033/175] Localize 'Executing task' messages Fixes #152076 --- .../tasks/browser/terminalTaskSystem.ts | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index 9515d758635..ce5ca3eee59 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -1122,9 +1122,15 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { shellLaunchConfig.args = windowsShellArgs ? combinedShellArgs.join(' ') : combinedShellArgs; if (task.command.presentation && task.command.presentation.echo) { if (needsFolderQualification && workspaceFolder) { - shellLaunchConfig.initialText = formatMessageForTerminal(`Executing task in folder ${workspaceFolder.name}: ${commandLine}`, { excludeLeadingNewLine: true }); + shellLaunchConfig.initialText = formatMessageForTerminal(nls.localize({ + key: 'task.executingInFolder', + comment: ['The workspace folder the task is running in', 'The task command line or label'] + }, 'Executing task in folder {0}: {1}', workspaceFolder.name, commandLine), { excludeLeadingNewLine: true }); } else { - shellLaunchConfig.initialText = formatMessageForTerminal(`Executing task: ${commandLine}`, { excludeLeadingNewLine: true }); + shellLaunchConfig.initialText = formatMessageForTerminal(nls.localize({ + key: 'task.executing', + comment: ['The task command line or label'] + }, 'Executing task: {0}', commandLine), { excludeLeadingNewLine: true }); } } } else { @@ -1154,9 +1160,15 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { return args.join(' '); }; if (needsFolderQualification && workspaceFolder) { - shellLaunchConfig.initialText = formatMessageForTerminal(`Executing task in folder ${workspaceFolder.name}: ${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)}`, { excludeLeadingNewLine: true }); + shellLaunchConfig.initialText = formatMessageForTerminal(nls.localize({ + key: 'task.executingInFolder', + comment: ['The workspace folder the task is running in', 'The task command line or label'] + }, 'Executing task in folder {0}: {1}', workspaceFolder.name, `${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)}`), { excludeLeadingNewLine: true }); } else { - shellLaunchConfig.initialText = formatMessageForTerminal(`Executing task: ${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)}`, { excludeLeadingNewLine: true }); + shellLaunchConfig.initialText = formatMessageForTerminal(nls.localize({ + key: 'task.executing', + comment: ['The task command line or label'] + }, 'Executing task: {0}', `${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)}`), { excludeLeadingNewLine: true }); } } } @@ -1256,7 +1268,10 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { customPtyImplementation: (id, cols, rows) => new TerminalProcessExtHostProxy(id, cols, rows, this._terminalService), waitOnExit, name: this._createTerminalName(task), - initialText: task.command.presentation && task.command.presentation.echo ? formatMessageForTerminal(`Executing task: ${task._label}`, { excludeLeadingNewLine: true }) : undefined, + initialText: task.command.presentation && task.command.presentation.echo ? formatMessageForTerminal(nls.localize({ + key: 'task.executing', + comment: ['The task command line or label'] + }, 'Executing task: {0}', task._label), { excludeLeadingNewLine: true }) : undefined, isFeatureTerminal: true, icon: task.configurationProperties.icon ? ThemeIcon.fromId(task.configurationProperties.icon) : undefined, color: task.configurationProperties.color From 5e7d488b7c3f9dc4a040d2ab31dfce0f843dd30a Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Wed, 15 Jun 2022 14:51:27 +0200 Subject: [PATCH 034/175] Improve error message when a module cannot be bundled and exclude vs/nls from bundles (#152188) --- build/gulpfile.editor.js | 1 + build/lib/bundle.js | 11 ++++++++++- build/lib/bundle.ts | 12 ++++++++++-- src/buildfile.js | 2 ++ 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 3d8a85fd7f9..3b3fe4942d8 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -34,6 +34,7 @@ const editorEntryPoints = [ { name: 'vs/base/common/worker/simpleWorker', include: ['vs/editor/common/services/editorSimpleWorker'], + exclude: ['vs/nls'], prepend: ['vs/loader.js'], append: ['vs/base/worker/workerMain'], dest: 'vs/base/worker/workerMain.js' diff --git a/build/lib/bundle.js b/build/lib/bundle.js index 8c1967d4c68..4cf08aef429 100644 --- a/build/lib/bundle.js +++ b/build/lib/bundle.js @@ -298,9 +298,18 @@ function emitEntryPoint(modulesMap, deps, entryPoint, includedModules, prepend, if (module.shim) { mainResult.sources.push(emitShimmedModule(c, deps[c], module.shim, module.path, contents)); } - else { + else if (module.defineLocation) { mainResult.sources.push(emitNamedModule(c, module.defineLocation, module.path, contents)); } + else { + const moduleCopy = { + id: module.id, + path: module.path, + defineLocation: module.defineLocation, + dependencies: module.dependencies + }; + throw new Error(`Cannot bundle module '${module.id}' for entry point '${entryPoint}' because it has no shim and it lacks a defineLocation: ${JSON.stringify(moduleCopy)}`); + } }); Object.keys(usedPlugins).forEach((pluginName) => { const plugin = usedPlugins[pluginName]; diff --git a/build/lib/bundle.ts b/build/lib/bundle.ts index a1130d4bbbd..4c8a3e14589 100644 --- a/build/lib/bundle.ts +++ b/build/lib/bundle.ts @@ -15,7 +15,7 @@ interface IPosition { interface IBuildModuleInfo { id: string; path: string; - defineLocation: IPosition; + defineLocation: IPosition | null; dependencies: string[]; shim: string; exports: any; @@ -444,8 +444,16 @@ function emitEntryPoint( if (module.shim) { mainResult.sources.push(emitShimmedModule(c, deps[c], module.shim, module.path, contents)); - } else { + } else if (module.defineLocation) { mainResult.sources.push(emitNamedModule(c, module.defineLocation, module.path, contents)); + } else { + const moduleCopy = { + id: module.id, + path: module.path, + defineLocation: module.defineLocation, + dependencies: module.dependencies + }; + throw new Error(`Cannot bundle module '${module.id}' for entry point '${entryPoint}' because it has no shim and it lacks a defineLocation: ${JSON.stringify(moduleCopy)}`); } }); diff --git a/src/buildfile.js b/src/buildfile.js index 6b49aa30083..f0f49f65459 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -32,12 +32,14 @@ exports.base = [ { name: 'vs/editor/common/services/editorSimpleWorker', include: ['vs/base/common/worker/simpleWorker'], + exclude: ['vs/nls'], prepend: ['vs/loader.js', 'vs/nls.js'], append: ['vs/base/worker/workerMain'], dest: 'vs/base/worker/workerMain.js' }, { name: 'vs/base/common/worker/simpleWorker', + exclude: ['vs/nls'], } ]; From 03be93691c2aaa77e2f5ec485a8b665cc29ad061 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 15 Jun 2022 15:14:28 +0200 Subject: [PATCH 035/175] SourceControlInputBox API finalization (#152171) --- extensions/git/package.json | 1 - extensions/git/tsconfig.json | 1 - src/vs/workbench/api/common/extHostSCM.ts | 2 -- .../common/extensionsApiProposals.ts | 1 - src/vscode-dts/vscode.d.ts | 5 +++++ src/vscode-dts/vscode.proposed.scmInput.d.ts | 20 ------------------- 6 files changed, 5 insertions(+), 25 deletions(-) delete mode 100644 src/vscode-dts/vscode.proposed.scmInput.d.ts diff --git a/extensions/git/package.json b/extensions/git/package.json index 00464a5d1e6..603b8d62ed4 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -14,7 +14,6 @@ "contribMergeEditorToolbar", "contribViewsWelcome", "scmActionButton", - "scmInput", "scmSelectedProvider", "scmValidation", "timeline" diff --git a/extensions/git/tsconfig.json b/extensions/git/tsconfig.json index 1f1c02d3356..13997275056 100644 --- a/extensions/git/tsconfig.json +++ b/extensions/git/tsconfig.json @@ -12,7 +12,6 @@ "../../src/vscode-dts/vscode.d.ts", "../../src/vscode-dts/vscode.proposed.diffCommand.d.ts", "../../src/vscode-dts/vscode.proposed.scmActionButton.d.ts", - "../../src/vscode-dts/vscode.proposed.scmInput.d.ts", "../../src/vscode-dts/vscode.proposed.scmSelectedProvider.d.ts", "../../src/vscode-dts/vscode.proposed.scmValidation.d.ts", "../../src/vscode-dts/vscode.proposed.tabs.d.ts", diff --git a/src/vs/workbench/api/common/extHostSCM.ts b/src/vs/workbench/api/common/extHostSCM.ts index db595e3c9a5..5b033610f37 100644 --- a/src/vs/workbench/api/common/extHostSCM.ts +++ b/src/vs/workbench/api/common/extHostSCM.ts @@ -252,12 +252,10 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox { private _enabled: boolean = true; get enabled(): boolean { - checkProposedApiEnabled(this._extension, 'scmInput'); return this._enabled; } set enabled(enabled: boolean) { - checkProposedApiEnabled(this._extension, 'scmInput'); enabled = !!enabled; if (this._enabled === enabled) { diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 8fcd4d47199..080d4b9f534 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -47,7 +47,6 @@ export const allApiProposals = Object.freeze({ quickPickSortByLabel: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.quickPickSortByLabel.d.ts', resolvers: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.resolvers.d.ts', scmActionButton: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmActionButton.d.ts', - scmInput: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmInput.d.ts', scmSelectedProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmSelectedProvider.d.ts', scmValidation: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmValidation.d.ts', snippetWorkspaceEdit: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.snippetWorkspaceEdit.d.ts', diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index cac3633e6a3..d701f46f418 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -13770,6 +13770,11 @@ declare module 'vscode' { */ placeholder: string; + /** + * Controls whether the input box is enabled (default is `true`). + */ + enabled: boolean; + /** * Controls whether the input box is visible (default is `true`). */ diff --git a/src/vscode-dts/vscode.proposed.scmInput.d.ts b/src/vscode-dts/vscode.proposed.scmInput.d.ts deleted file mode 100644 index 6efdb57ae45..00000000000 --- a/src/vscode-dts/vscode.proposed.scmInput.d.ts +++ /dev/null @@ -1,20 +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 module 'vscode' { - - // https://github.com/microsoft/vscode/issues/150268 - - /** - * Represents the input box in the Source Control viewlet. - */ - export interface SourceControlInputBox { - - /** - * Controls whether the input box is enabled (default is `true`). - */ - enabled: boolean; - } -} From cd3dd9e9208ccece132ded84556c863d179f1ef8 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 15 Jun 2022 06:46:06 -0700 Subject: [PATCH 036/175] Create output channel for (local) pty host Fixes #151159 --- src/vs/platform/terminal/common/terminal.ts | 4 ++++ src/vs/platform/terminal/node/ptyHostMain.ts | 22 +++++++++++++---- .../platform/terminal/node/ptyHostService.ts | 2 +- .../browser/terminalMainContribution.ts | 24 ++++++++++++++++++- 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 92887082c44..2a773941b88 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -113,6 +113,10 @@ export const enum TerminalSettingId { ShellIntegrationCommandHistory = 'terminal.integrated.shellIntegration.history' } +export const enum TerminalLogFiles { + PtyHost = 'ptyhost' +} + export const enum PosixShellType { PowerShell = 'pwsh', Bash = 'bash', diff --git a/src/vs/platform/terminal/node/ptyHostMain.ts b/src/vs/platform/terminal/node/ptyHostMain.ts index 15812b04d0a..1e98aade541 100644 --- a/src/vs/platform/terminal/node/ptyHostMain.ts +++ b/src/vs/platform/terminal/node/ptyHostMain.ts @@ -3,11 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { join } from 'vs/base/common/path'; import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc'; import { Server } from 'vs/base/parts/ipc/node/ipc.cp'; -import { ConsoleLogger, LogService } from 'vs/platform/log/common/log'; +import { OPTIONS, parseArgs } from 'vs/platform/environment/node/argv'; +import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { ConsoleLogger, getLogLevel, LogService, MultiplexLogService } from 'vs/platform/log/common/log'; import { LogLevelChannel } from 'vs/platform/log/common/logIpc'; -import { IReconnectConstants, TerminalIpcChannels } from 'vs/platform/terminal/common/terminal'; +import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog'; +import product from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { IReconnectConstants, TerminalIpcChannels, TerminalLogFiles } from 'vs/platform/terminal/common/terminal'; import { HeartbeatService } from 'vs/platform/terminal/node/heartbeatService'; import { PtyService } from 'vs/platform/terminal/node/ptyService'; @@ -16,9 +22,15 @@ const server = new Server('ptyHost'); const lastPtyId = parseInt(process.env.VSCODE_LAST_PTY_ID || '0'); delete process.env.VSCODE_LAST_PTY_ID; -const logService = new LogService(new ConsoleLogger()); -const logChannel = new LogLevelChannel(logService); -server.registerChannel(TerminalIpcChannels.Log, logChannel); +// Logging +const productService: IProductService = { _serviceBrand: undefined, ...product }; +const environmentService = new NativeEnvironmentService(parseArgs(process.argv, OPTIONS), productService); +const logService = new LogService(new MultiplexLogService([ + new ConsoleLogger(), + new SpdLogLogger(TerminalLogFiles.PtyHost, join(environmentService.logsPath, `${TerminalLogFiles.PtyHost}.log`), true, false, getLogLevel(environmentService)) +])); +const logLevelChannel = new LogLevelChannel(logService); +server.registerChannel(TerminalIpcChannels.Log, logLevelChannel); const heartbeatService = new HeartbeatService(); server.registerChannel(TerminalIpcChannels.Heartbeat, ProxyChannel.fromService(heartbeatService)); diff --git a/src/vs/platform/terminal/node/ptyHostService.ts b/src/vs/platform/terminal/node/ptyHostService.ts index b7679d75f90..5af2d312b6b 100644 --- a/src/vs/platform/terminal/node/ptyHostService.ts +++ b/src/vs/platform/terminal/node/ptyHostService.ts @@ -133,7 +133,7 @@ export class PtyHostService extends Disposable implements IPtyService { private _startPtyHost(): [Client, IPtyService] { const opts: IIPCOptions = { serverName: 'Pty Host', - args: ['--type=ptyHost'], + args: ['--type=ptyHost', '--logsPath', this._environmentService.logsPath], env: { VSCODE_LAST_PTY_ID: lastPtyId, VSCODE_AMD_ENTRYPOINT: 'vs/platform/terminal/node/ptyHostMain', diff --git a/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts b/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts index 1529008404b..97a77d4e9c4 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts @@ -3,26 +3,40 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { URI } from 'vs/base/common/uri'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; +import { localize } from 'vs/nls'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IFileService } from 'vs/platform/files/common/files'; import { ILabelService } from 'vs/platform/label/common/label'; +import { ILogService } from 'vs/platform/log/common/log'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { ITerminalEditorService, ITerminalGroupService, ITerminalService, terminalEditorId } from 'vs/workbench/contrib/terminal/browser/terminal'; import { terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalStrings'; import { IEditorResolverService, RegisteredEditorPriority } from 'vs/workbench/services/editor/common/editorResolverService'; +import { registerLogChannel } from 'vs/workbench/services/output/common/output'; +import { join } from 'vs/base/common/path'; +import { TerminalLogFiles } from 'vs/platform/terminal/common/terminal'; /** * The main contribution for the terminal contrib. This contains calls to other components necessary * to set up the terminal but don't need to be tracked in the long term (where TerminalService would * be more relevant). */ -export class TerminalMainContribution implements IWorkbenchContribution { +export class TerminalMainContribution extends Disposable implements IWorkbenchContribution { constructor( @IEditorResolverService editorResolverService: IEditorResolverService, + @IEnvironmentService environmentService: IEnvironmentService, + @IFileService private readonly _fileService: IFileService, @ILabelService labelService: ILabelService, + @ILogService private readonly _logService: ILogService, @ITerminalService terminalService: ITerminalService, @ITerminalEditorService terminalEditorService: ITerminalEditorService, @ITerminalGroupService terminalGroupService: ITerminalGroupService ) { + super(); + // Register terminal editors editorResolverService.registerEditor( `${Schemas.vscodeTerminal}:/**`, @@ -66,5 +80,13 @@ export class TerminalMainContribution implements IWorkbenchContribution { separator: '' } }); + + // Register log channel + this._registerLogChannel('ptyHostLog', localize('ptyHost', "Pty Host"), URI.file(join(environmentService.logsPath, `${TerminalLogFiles.PtyHost}.log`))); + } + + private _registerLogChannel(id: string, label: string, file: URI): void { + const promise = registerLogChannel(id, label, file, this._fileService, this._logService); + this._register(toDisposable(() => promise.cancel())); } } From d649a6bde4e1661b2335c1280bfeadc98a968521 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 15 Jun 2022 06:50:59 -0700 Subject: [PATCH 037/175] Setup remote pty host output channel Fixes #151159 --- src/vs/platform/terminal/common/terminal.ts | 4 ++-- src/vs/platform/terminal/node/ptyHostMain.ts | 4 ++-- src/vs/workbench/contrib/remote/common/remote.contribution.ts | 2 ++ .../contrib/terminal/browser/terminalMainContribution.ts | 4 ++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 2a773941b88..202760e12f6 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -113,8 +113,8 @@ export const enum TerminalSettingId { ShellIntegrationCommandHistory = 'terminal.integrated.shellIntegration.history' } -export const enum TerminalLogFiles { - PtyHost = 'ptyhost' +export const enum TerminalLogConstants { + FileName = 'ptyhost' } export const enum PosixShellType { diff --git a/src/vs/platform/terminal/node/ptyHostMain.ts b/src/vs/platform/terminal/node/ptyHostMain.ts index 1e98aade541..3de63e7d9e6 100644 --- a/src/vs/platform/terminal/node/ptyHostMain.ts +++ b/src/vs/platform/terminal/node/ptyHostMain.ts @@ -13,7 +13,7 @@ import { LogLevelChannel } from 'vs/platform/log/common/logIpc'; import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog'; import product from 'vs/platform/product/common/product'; import { IProductService } from 'vs/platform/product/common/productService'; -import { IReconnectConstants, TerminalIpcChannels, TerminalLogFiles } from 'vs/platform/terminal/common/terminal'; +import { IReconnectConstants, TerminalIpcChannels, TerminalLogConstants } from 'vs/platform/terminal/common/terminal'; import { HeartbeatService } from 'vs/platform/terminal/node/heartbeatService'; import { PtyService } from 'vs/platform/terminal/node/ptyService'; @@ -27,7 +27,7 @@ const productService: IProductService = { _serviceBrand: undefined, ...product } const environmentService = new NativeEnvironmentService(parseArgs(process.argv, OPTIONS), productService); const logService = new LogService(new MultiplexLogService([ new ConsoleLogger(), - new SpdLogLogger(TerminalLogFiles.PtyHost, join(environmentService.logsPath, `${TerminalLogFiles.PtyHost}.log`), true, false, getLogLevel(environmentService)) + new SpdLogLogger(TerminalLogConstants.FileName, join(environmentService.logsPath, `${TerminalLogConstants.FileName}.log`), true, false, getLogLevel(environmentService)) ])); const logLevelChannel = new LogLevelChannel(logService); server.registerChannel(TerminalIpcChannels.Log, logLevelChannel); diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index 7da92791ee6..b6488780711 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -32,6 +32,7 @@ import { getRemoteName } from 'vs/platform/remote/common/remoteHosts'; import { IDownloadService } from 'vs/platform/download/common/download'; import { DownloadServiceChannel } from 'vs/platform/download/common/downloadIpc'; import { timeout } from 'vs/base/common/async'; +import { TerminalLogConstants } from 'vs/platform/terminal/common/terminal'; export class LabelContribution implements IWorkbenchContribution { constructor( @@ -99,6 +100,7 @@ class RemoteLogOutputChannels implements IWorkbenchContribution { if (remoteEnv) { const outputChannelRegistry = Registry.as(OutputExt.OutputChannels); outputChannelRegistry.registerChannel({ id: 'remoteExtensionLog', label: localize('remoteExtensionLog', "Remote Server"), file: joinPath(remoteEnv.logsPath, `${RemoteExtensionLogFileName}.log`), log: true }); + outputChannelRegistry.registerChannel({ id: 'remotePtyHostLog', label: localize('remotePtyHostLog', "Remote Pty Host"), file: joinPath(remoteEnv.logsPath, `${TerminalLogConstants.FileName}.log`), log: true }); } }); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts b/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts index 97a77d4e9c4..762ca452ce7 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts @@ -17,7 +17,7 @@ import { terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalSt import { IEditorResolverService, RegisteredEditorPriority } from 'vs/workbench/services/editor/common/editorResolverService'; import { registerLogChannel } from 'vs/workbench/services/output/common/output'; import { join } from 'vs/base/common/path'; -import { TerminalLogFiles } from 'vs/platform/terminal/common/terminal'; +import { TerminalLogConstants } from 'vs/platform/terminal/common/terminal'; /** * The main contribution for the terminal contrib. This contains calls to other components necessary @@ -82,7 +82,7 @@ export class TerminalMainContribution extends Disposable implements IWorkbenchCo }); // Register log channel - this._registerLogChannel('ptyHostLog', localize('ptyHost', "Pty Host"), URI.file(join(environmentService.logsPath, `${TerminalLogFiles.PtyHost}.log`))); + this._registerLogChannel('ptyHostLog', localize('ptyHost', "Pty Host"), URI.file(join(environmentService.logsPath, `${TerminalLogConstants.FileName}.log`))); } private _registerLogChannel(id: string, label: string, file: URI): void { From 1f5dc6a5729795f80b92f6de5a76d78462640662 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 15 Jun 2022 06:59:40 -0700 Subject: [PATCH 038/175] Run shell integration tests 100 times in CI Part of #150478 --- .../terminal-shellIntegration.test.ts | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts b/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts index 47d624633f2..0bc30da5b58 100644 --- a/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts +++ b/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts @@ -28,38 +28,39 @@ export function setup() { await terminal.runCommandWithValue(TerminalCommandIdWithValue.NewWithProfile, process.platform === 'win32' ? 'PowerShell' : 'bash'); } - // TODO: These are currently flaky https://github.com/microsoft/vscode/issues/150478 - describe.skip('Shell integration', function () { - describe('Decorations', function () { - describe('Should show default icons', function () { - it('Placeholder', async () => { - await createShellIntegrationProfile(); - await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 0 }); + for (let i = 0; i < 100; i++) { + describe(`Shell integration ${i}`, function () { + describe('Decorations', function () { + describe('Should show default icons', function () { + it('Placeholder', async () => { + await createShellIntegrationProfile(); + await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 0 }); + }); + it('Success', async () => { + await createShellIntegrationProfile(); + await terminal.runCommandInTerminal(`ls`); + await terminal.assertCommandDecorations({ placeholder: 1, success: 1, error: 0 }); + }); + it('Error', async () => { + await createShellIntegrationProfile(); + await terminal.runCommandInTerminal(`fsdkfsjdlfksjdkf`); + await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 1 }); + }); }); - it('Success', async () => { - await createShellIntegrationProfile(); - await terminal.runCommandInTerminal(`ls`); - await terminal.assertCommandDecorations({ placeholder: 1, success: 1, error: 0 }); - }); - it('Error', async () => { - await createShellIntegrationProfile(); - await terminal.runCommandInTerminal(`fsdkfsjdlfksjdkf`); - await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 1 }); - }); - }); - describe('Custom configuration', function () { - it('Should update and show custom icons', async () => { - await createShellIntegrationProfile(); - await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 0 }); - await terminal.runCommandInTerminal(`ls`); - await terminal.runCommandInTerminal(`fsdkfsjdlfksjdkf`); - await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationIcon', '"zap"'); - await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationIconSuccess', '"zap"'); - await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationIconError', '"zap"'); - await terminal.assertCommandDecorations(undefined, { updatedIcon: "zap", count: 3 }); + describe('Custom configuration', function () { + it('Should update and show custom icons', async () => { + await createShellIntegrationProfile(); + await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 0 }); + await terminal.runCommandInTerminal(`ls`); + await terminal.runCommandInTerminal(`fsdkfsjdlfksjdkf`); + await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationIcon', '"zap"'); + await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationIconSuccess', '"zap"'); + await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationIconError', '"zap"'); + await terminal.assertCommandDecorations(undefined, { updatedIcon: "zap", count: 3 }); + }); }); }); }); - }); + } }); } From 6bd36f529510e418e6304cdbcb7a846ef7eb695c Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 15 Jun 2022 16:13:17 +0200 Subject: [PATCH 039/175] update seti (#152157) --- extensions/theme-seti/cgmanifest.json | 2 +- extensions/theme-seti/icons/seti.woff | Bin 37204 -> 37192 bytes .../theme-seti/icons/vs-seti-icon-theme.json | 18 +++++++++++++----- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/extensions/theme-seti/cgmanifest.json b/extensions/theme-seti/cgmanifest.json index 00e2991fb96..8bcfb2d2f40 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": "8dba1bc311dad1b9bc23c4779149f3bf9baa8cb0" + "commitHash": "6b83574de165123583d6d8d5b3b6c91f04b7153d" } }, "version": "0.1.0" diff --git a/extensions/theme-seti/icons/seti.woff b/extensions/theme-seti/icons/seti.woff index aeb87b845af6596a1b72e1590613ea35a538cc52..70e9074c0fb5e511be11eac50abb6e2f95d1c4de 100644 GIT binary patch delta 33632 zcmV)FK)=7#q5{aG0u*;oMn(Vu00000kw^dw00000+?0_NUw?1_0K~wJnlm_PWnp9h z0E%D$001rk001^1kH&mxXk}pl0E&2DAwZibjce2_$-Pc=+(X7!)*Dqmf{)xjaeWYT}T1V`B8` z4F)1N`Xe!NANAh)_O8>7@9BHL*LA9D*B-xNeQW*yYpw6=9L>Q$V&G?TMy{CKf1cZy z%c151>aU@075Nq-O$+%|)a#Ch{Z2ayvsVozi>Tdc&!Ql4d)Yo;aCmi+U>yxd!_KhN zPSA_z-*Nu=cl@sWXl#4U)~&DEcI9B(wt>c&&J~XF?(2G-Q-u{@^(Urldb(xmdD9tg z+a?qy5V8bEN4uB2{*qn~KDubWf4RDQHK{f3XjZ?4F`<+Za`OpIBm6CB6%nC2{#M`B z1U)rG2s2^0oIv$(T1P~da>sHvLj8CkOKKs(XHjzzx09yeHsYP6*=cXS2L1L9)F1Un zYiKlFMco<-dtt8&b?atrU?I4qg8VR$zmlbh1X&Akkqr8y;h9$mPtGT{f6r`YXr5t~ zZDD(AY{G}>sGjZd(D77G2v@dE$4*VlhbHHaYL=rNMMYPNCAF;*l_(XfK+!#+Us4Rk zu?ZGb^#fb6OoFcBd}+bAh)FnCIi;L)fmGG;jM7wlI<}d|%7$im?Q=CD;8|SNIH6o` z@OEAl%n-`fsMr=lUyiGdf2zePCU$6Qx-eo>r2>;2l5L3p2WjMVXu+P2dT!)KBX@*< z-EqLtGP_J7Vz~sk<)U++_|}&H|j-6ClRAwCmKL| zLh}wlu2Nx=t=8*CLE0a_6tXG`d}z?5IU4k$Q7=daBIyKQxM=pGf4S(Au)ec?>r%yzrSP0rC0nx+fkUN7+g~n z$8-FuomZv|uKHZj6t5mSMUm&<`^ZPW_sD}E`sAbz^Y|Zg<=k5CvQ&PWP~1ff(lY2v z(HM+|AOeBwi$t_Ge`NqfDU%%-3hmLbo61Z8wd?qsNBb~H|V|D80nQ%BYEJiJ#S~_MZ$LZl2jOv+Oo19V#H4z6ij6%2nbqSKY7IDib7bazGKNW*5*{ExoV+Z z8WrH1ilD7jpefV#w8;6n8**>UeH>bIG=zbc4m#OxK^J5ofW8)TfJGfY38E$(37}De zLd29_kK9ATxtu)(?xQ4}(??_GEGb z^a&qj{gsZ|b<~$P8usLo-k=AczGx1TfF~S(VOxPZ?dtKXE}Uv0iYNmeWca3FFye@( zBhYBDL0w&?{XP~L)1Y|Zbr{4GQ$~e}1uyDh=+e0$f8VloTy~e2Uq?J&?BBjrSly`* zytJ@$hn;VlPClxv&SETdp>$n`hxuZSKyML^(TdIR>jv$4p(=G7x-W_IP!K2z;kE_} z5pzsD6)BX%CR~&U)zbDTg8@%>^`87ZVo0h^tU7^()oKit$F6P~m_ZM>rVgA}b5-U3 zrJ6Yve-Ux=3ax~xrmOQ$M2;6>>RBCfBU2HlL^GHz3}))WTvTMYqDkLcFy3=08byJM z2I$m1_q=EP?D$z?zUMvfxksvqdva&xz{-_!Q@MFKzcK2Kk`5?_L`2Z}fiGphyN3D; zh&9`N(7QfAvw=S{{@3+W;fY%9M0jd<`3sscf8Y4{W81pbYIpn=uUu}G%jjsa)#^`8 zq0c_JxVW%*<>K>3(@BQRv8&x}z3M&Xb{YO^Q+;_>PRYhO(Q?0*dw1^lb5G~Kfjm@3 z9Vl695EfChEnuWp$*7l&>vhzYlCwYt=?SSx$&6z-ltX`F8me$|6prMsnzW3AQ6FwF ze;7(7B1ibB3zh|hPM!io0S+yo4p^oI0wW;|))3sGhL)tR=%um?@-BD)7Y|bVl3LyV zP)K9dlaGtwdMHf?9t`%%hYLX7+a1|#viYL07j;7@YbOa)<=Rdxs3J(Zw1`lXetQt6 z1OhyYo7@jOP{l~zf3wpj4gs6A<;X1cK{Gj+V2ZoqA(aE)Hh0twU*>KzNKtu@09V;1>c^(WCMdg;zz<4W^YNkT+U@zf*`2~wt z0ubRsh>hRzVg&LICSH-_3alQRHJil(CxT+Y6IgK+Y6#s7EX{Jr5rdNXDX^f#eXAj8FrlroAt#;{eO$S6$X#@RY52-Jg5=&OJF2fcWT$x1HG)+bo~&Amwb>%)wg>Vd+h!y1e1-ZWCIe2jG6%ONC+5p)|Y}F^%lUSreG74Hb^Bl z(fZ5pJ#k`veH|4C)3rtIq|!gM&^^}e9(&#K!bhhE=z%M*M8)yD(6KAmr-w;-ZqJq! zC-$}sH}wntNp3E;KX(w2e@9=ATKOQ6;tc>977lzEF92;vQ92Sf!Cxy8$tW50(sxg! zZ!kXco%o{;?Eg+{Z9W+-9*B1sGJPv=+P`OaVM>}@o6 zCOh}VQuvweW^SWeA_xk~P0xG%`-Xuw3!^0v2aAOjN~ z0Vj|owHZJnQo0~Dc`KzI99jlccU7wOs3-4$UTpP?Tbl7K5FpL=r_VW0IW%*_Ys`Wl zhgzUvK^{0LfB4#obBZ$}LsH@Kg&YCqbj?+#?2v zf)FMPnFA7UiG{;v1~Y`A2$Ar+zgC{_e+z25QG;` zM7&JAdIBU;wZR(B99SscVp)MYy$_Qc5hwxBlSC0g0riuH5hH)t5W)x~I)KX!or|-5 zDKY0DcV6zblE-G<11}$jFSMv^RLKS;{z7RFz-!o+uxx^ax*(cdlJit^W*Q%hs5H-cvE_HSI*$Cew+TUDFuuAr^bZ&0g^oP?p)# zw=}AhjK8;j%{tn7oCe1CTlDYXk5}zS6>npr-)WD^p>h3Zvt5H>%~6^&)K&gx}jAE zqGOQRh{1orAo-<>mfBN|5-F6on)wUfdVZreQaO6^oKeWY{6G=l+2?I5M_Yucu?ybX zYH#Ytuj4-kJ6y{&1P9$OBs5-}$PK6@iTp%Ibnw>^IRG&3Lm%J@b6c@c$!({YV9)8% z!e@>?yzMA@GbVe$F#XQ`jJZjHF?!2u8T}sG8qR-x;-U|ZE;?(g6S6z!!ZVk1>(aY| z06zh~iAGhlh5=5w&=G4`HnD}tFFXdKFKG+&B7)a;-Z1dy{-*k0XRkSRFtOKPJse%J zB|LcD>vxoT=S~f;n0Kc>Q~ZOe<41Q^%>JSIh4YjA&dZJzt9mxx@q;Q>t?Y-qFi8 z@>A{k>pDU7kEpOV{^40Ye;tg|SIIhc+9_$z*i|o6fIv;8&;SF<$d`29wbLV6b9V;) zBy+^@>|zN^79_cc5xj;b9tQyo2wlV!U(tUS{lshP(cJZ?Z`srKMBq8Kf-XH@NKlm( zm~9GL+h8$M2_R+Ub(1Z{a_k4(*Zo3e_UM&Sp&WayVo?WH!omWKsssoMJliPEUa@c~ zqpx0E@ob4N6g|*fP=<&g)!Z9O=B@}sIMX(>+C>RTuni}yYl7JXEDd#-unjoO3=Is( zFS-d}6}X9TKx6!(n{Xg;2F-2)KZyP>^Tz_OIhTH-r+>*6CNNhnuh2MC_5a&*IRmoF z;7WL|Q7!+!JT|=voT(|?`nj7lMOt_%U0LDH;?~VV!RJ}*IR^f+lG_En{QrN8zXVCp zlm4qgG8&xy0Z@9!e6o_#7FX4MGxP$WEzUI17ISxHy0th5w!$)n8KU7_w+nw?7_6=i zwqv?_Vu$avB~2lOt9mj7n&RhGJ%6a-=WVND7p&Ucy@R=xl{vDuHrUu0Y|7~lawD)l z0FoeeE^E0RvWOZ2z4*UAjP`$_eGk8M$tCFcCFI6ey=r{d6HlPGylQ;m{rBHLseg8@ zne$$8b!P-x%!5`Kbq~fFY^vxnQoo7k2%OWy>WkV=WXzB=fREB zr%yj|=n!zG3}5IIo@+z7qx4z8cs;k_GPwO{@|a)r{cYSGPk-bi-~L5^M0@!gc<$zz znVG@ndwP00$1^!04A@#LHw|{+K<=27mJ}LCB6CcoFmxg&seE)~HG2_Qqh2aQy;Lc} zwk4xKJJ2fXRxwzOSu}qN;27?M#Xw}bb?f@>{_go!qY&yEE=I+AYYXf+=Pm6SJRfVb zT09pM$Nrxos4P`zG-N@;S8tfH9a8~WF}FmHu5n5=&BAow+UlC33=UQi4*jHY!G`NI zmK<6&w6z`Q&|2*shr8Xw`&_1K!tf95DHUups_Yd`X{u<88;*YyC117mGw~hh*B5eH zE)RA00lm3^F@P5I)2$)?&If<@!=HZ7fp+WkL+is!j-r=te;aynqk8%FkB&cg_$)Ro zlM>uPwq!2Vq)8J-a^Tkxj2_AJWZrMg+P#NsU`_=4{CyptGYic9I@TjX`z*lm%eF#cf?9 zG=#~h*7W-4v@dwW#h?My*qqx|u|_}DnNu}?^>=Z%xTSw#7PPhfgNqOB*n5u7Vk2 z3s(-E0dV>WACN9mXw5VNF^hN##aT!_LSFlt#i&!iija`ynW1`$Te0G9+jl%)t<98* z`KDWr4Qu>Q zxL^;hiXdE9j9LUth$<1VHBYfrY?NZKK&`UiYFKU&8jn9I(TnF+k|v^vtgP@8O;5H> zO6tk$@!$ReJ#`_--qn&BrA8xtcYeWlOTOEQR3@pH2O1X|vM8?Hnw-U^JMsU_H6`+bVVE++1vwhi zSQJkk{<7i&`AKGj^UBn*!Z|bF+iDs#3IoHSYXeBk)P?* zD*0h=ZRgT3@to<_MG>ae@_7>-H^yHBjc>^~Y9OMZ!^8jta}YiE)B_JZh5e`A|Neic zaQOM-ciwsY^8~)gotJ;^jDGwIIRrgCEj@ahVSJIn>P)D!LPKAYRDE%~E2m8+%lxrZ84)%mHFdFOp+s&l1c|FX(7X6GAd*RVF_ z8zQvha^USZ1wVJCG3S`UmXd9c-@RGY%;_*irl7_wr%Xm#n9HfC(TN5vX3j8XoHmMp4iTA`m0FC%?Y zISf*QocUX18DStT5UH0l&p;6N(*8$x-AHa)4JxO1mOBP<~52~cS zBS#gIql%1z778K}d>g5E-hJ{UnCKtfeK+=y_Kv&W@eX91eBw&jy!#z@pVbHRWQRn5 zK$!$!s!?ZzUOf4&54L~5^riNLe@(VOS$p7HS6%h32Wp$P4Zlc^!#I{c_OkS;bYy}+ zz&xoJJ!#ryLwLjk2#0s=*=UNq82|Lkf33P?_kU-muIudkU;gr?*cAAZjkjN*>tFj< z^ya3<&G8NIA{baFc?Wv)`0aR$NsP0;f?p+G?*7~-a(|S2Cij1h)Qc;UVFv&rmzubp zh9k4a&)oY_2<4b$Gk}>JWWF3R$`D1Tmx7Aa&Sn!=Z%jG@ZgbCKPAHr2au`Xr_x zi#8-TQ>#aemZ1iEbmD2~>IYEfV>U_Oa(D40|kM5I(0xacUR?kX-J zRm;?IO~&q3O?S%7ER(#cDUFuJ2x75arNKf-l?6bC22v-z{*d@yf_1Gq0@1a7PP`GrP z5>@g`#lY2N8z84ZfzxhLyBB#Lx40UK=79=jWLK)#uc%ucV4U)Gj9v|mrA(Py7Q}u|k^IN4IZbFx z9Cb=g;1&vJ$II8qU1=Us19W%-fX*GZ$z9`{=Nsc|$m4j9IPJ##|M6XXqA`Eg59#7( zz~;7cdvoW*mB|E~)liV8cdVl{4NXEsiE%rFMJ#_~eJ7T!zay}r$Dwz=L* z-FpG6=bZqP0pV~@lE(8CA8z_Wx%`hR5eQE8iuUg_#hA21pKQrplg4-^!&|0@4FPs1 zS==LOr&aDu;AfD*t|;~9%k+rlGzQs8WKc3yY26WEhY7M+!=Oy$saXhR2Z9ISIZ=NQ z>p{sPoat1P#7GyhNBI7IP(@F*ZG!=+1K!0zXBZ1pv14*l3=57^wizNkR<(Me?3il1 zwYND%$*!ArUwFa(H-^6NU$fsZ322I|;7Vt4xMmoN1@NSaDaEEi@_ArTK!Y$b>J6wy zsmep%=t%!jOpwNSp-e$#*%cp}aJzpH*owNlb|2ky>qV9w$2T9m;DQ4;$4s*h_%@j7 z40G6ExADYs`?;cwm$kgaBgwK9Cq{amc2ks4Z&|v3nU_fWgI>J)FRs4&eKkxua(?!{ zHSFL&%6^XjIs5q$bo=;i==NI_hy4iM{tv49@`JDZB~b_1wtpr^0q4ZIPHulWcQ|)4 zcXRG-xt}2gg=m2Gpi9wf&>PUNptqoRp?lE>B+pIzM0TE#gtg;`-CCX>*2AH;p7DS$vMm6fR&MgSO1=0s`HYrX4y` z#wgZE7?7lsCflv2^3<1pR2Y9WlWfG{04^M*!MN0+GD;1R{7!>CK?r)p52LP3e@)RT z6ey9pWiuHx1Geo&oRt%Jx^%b-?xi8Ye#RTBNbb&vgEVeYQ+i_~>7NaF*qw;090yGp z1?_0iUm!ey3;l2eMeKkUwXjGXInp66E1J=*QNQW;;F64bbwkmGW0HT?hEY>i3EE2L z0FC@+zZZm{J^HW*>VdZx6GnY&Ht+%*1=>#C)^(xiZ!G6+p_tPEa@10V7?uHlR~aS@ z3Z7>e#)yI~%j5wAPzI7Clf+C@!wMK&Ca8)@lc``j0HbIeqcSi%hM39F$2jGLVciu? z4b+ln2vxPDQ<$q5G!=i0pTz=Yh2p^@OEpFF+Ice|2C#11VN78vFl8A=BU)7 z3O6~Ylr609TO|YrvO=G8xEGQjS+IP{l6Tpak}BGTDOVA8e!I!RUcfC(tXYO-gEcU9 zMP?RR)I^{XWRVpn^AWfK;~lFE%3qYtV(U$?wjapaLWT2~L|}i{YedD&F}MLJU=dFE zC{S=P%8wNbMc-A0sQX??kQLQ5^8nM9EwhuaGGK@Cf>=`x!!r!E2D+9IlGhj|6m%rd zn*b_=pp-PZs$tOCOqfvUE@uj;2-Cm_N~M9_q1eb1nbCvrxI`FZrlr%$vK1I{ey?Xx zI7b6Bwq6^~8kB#C*oXszF<&WJyr?kEY}MiDmeJleCD@_zk&F|f<^#Ryl%hdGrBzU& zN+y?TKV~JOQbsVgh$wk}-O_CzYI-A^F9#uJ)qKaaP5^)cQ~`)d(PSzI{0vmiIT*ns z4$cazS_%`;(1r;=B!;nNI+fs~`<7Fr`kOOcXWkDu5 zAvoh&OQ~wA6QYTE$+CGfung)%+UHagfzr4P6gr|GB1pmb2kG{o69lhQY1Za(xqZD; zY*I>fZF+xt0X2&AHRBRNiJ?*P5JF02`QWbJvZ5?3U2twUap}8v&P^-&VqyEt{%aL| zZT1|+z7p|4ecj?wVQOlCtJuKUAk*?oCo@FHL?f4h)do2QOJ1ywiqnKAj>5~nFdSvM zy;B1lwszk3b<1c*)9l|+JL&CRt0%(TyLNni4hDbrA0Q$~qp4Wy#YNrmO0tQ?#OF57 ztph?koI8=bJ@=cr`(!i}=z8X^lqemvG3cD2Ce9rJ&bmZPT)C%RCm=>+_8U@QL*M zTA}Nfu0H}-ye7SrAx=-i-7eaD?seClyLbF4q`AJUp`aQ(7F5Hq8sL{zCn!)3IL2_TJhJL^?GyNe}m@gx~thiV8a_8z8RBwBcZ@}uY=y{EQ4JDTQ*89kuSY0 zX_U#I2=^qfHQnD!q>~iP;BuHbprO?>^RBT+x3*!p39o7dgtSD%y2Mfx z!v^dHT3IKxf@zrH;qE0@+z5(}Ryu#33gw&Mg6>$}R@vA+8cc8c46X$(GMzh^yGb&U zG@?4GW9A_qHC|!;)5t0Sip?-|knNStyLm~o(;cSCOslBX>t?e{(iCWDLWvnNt&`px za1Hu_O0=4Qf(p>Sj9nJBTpy#agzXELDbba@V7ARd7~beD6pTRJtz%A9!QFpm*;WBi z5^OqP(Foo>4c6rF%lU&~s5^(ic6hd=oOE^zD0_nmW;mk~LLmJ_1WQ1G;%IQs_Od!{Sq6L7i>uaq(2eMWm_EeK<$2Sj>S3;tVyjg _vWY0fU&a8wxC(TmvRD8EQ> z!wiDuBJ?9i8^AQjPpDx2DXNvc0(%H;m-*1+Cmv!jn#MPK-bI9*@wJj1na_PR_b{|( z=9HhfSb-sR2JJ~`sh@u=PaG~%G}Cl~bY2w5w%jKf*a!$!0{n^xS#p$2BuHtOfc+_e zP4lSIAWNDoAYrgfR+ky=socx>RX4~YDp_=3Gc6QOk;oVnwdmn55)o*orkMb_EXS{< zX<&xP3zgWB9~A?~qdLg6t^>>yh((4)Wm*OSIRtuxfVan@JScz1Jmvtlh(R3-V}PF~ z)3MMDCyoJaI4&+T%bp^i0<%c?U7Jvpn!-$X8*ewsNV$I&3{fEDg$Bq+^2 zO#OISB&-*~;7PB+w}3Tt^aVWu@Y1$5f~omypMW+C>&-%y^LFSmL<6{76Xdg>HN-K@ z88BbUbz{-c6rmcHGSH}HJ_r?#k6dv6Ayn65^`y&)Lx`zrJYI}--=GcjiQstngOjx^ zDh0;piGK)_h<>adSCnV$;Mef z<~;sdlc+8@U3~V9#9D=}gb(t@!pSH)S!i9adHSjSyRRqaf*B-Ed38I`r6(6(JiYh` z{#xn!(uH@POTy!!^wq)_AyfBGrSI`B;#RB&$(ybs`91ksuS8}qxeWi0z2tF(_L7r5 zFD!p2r%%JjnXM}yeQtbk`}2=J`aJrB)#uQge*S2>bw&1>H5S9^8L@!Qq{o_H&+))SH%TrBN^;-Nq5Vcl^|Y z52BR^|MAeF4;?!651@6e@lW0*|80)(7x4cMl4t&0ytXrlUV8kVdmcwGe&N0E{Q~wL zJ$(A~;YZ1%M^2wU^5{fHLi}}T)#=>U+!1NB2hz~;)S)%8t5Gvc`;eMoUC!KZ?qm z`e=!40a@$j_T?^0ZIRSOvl&^bpjRYwNS)LkpynivHcg$E4)jq|PD1DmU_0og)O;2O z|Ddy3S~Ab(8&_;?RJxa)e=OwM?C7Gc3*BG2aHl8d367uIyJP$AJv)Ea_wBhgnZo15B6BHZTytt!ukSg$ z4%lxYF+_act$UNzDb(DyM}Ds2+w;4Y%;D_j73sA{_m3Y#t>&%2_3L}koToOI=*rf; zx9&#om|V4O&yHlGlNQKU;Gb`meFh~RPA1XISJDi!U`UV!NhW^>V0RYZmBycoB*lOd zA8rPDQ;aX;@o5r|48sCK91i3Z)pWM>TDlB^1nZKNPNK4M7nxTX$TJWVPKCyq&^&;L zfVWkuxgxUcqGS3_(ZGaS153$smD+^VOT`)uS-aS*&ee)}Zh-Bk9P_E7mLIfN3{GYb z9dM~83P@LUQ!Rfl2%4y*UlqFWcxPIAVbN`itDffAUW<49W>II{0|Thf=my|Wwb5v< zmqJwluom&D*eD3g0%TE=xoDW^_Ec@zn+4Pk52cP_9y{PMMKdIc&??l-SEsP8P^`rj zV?$#$5D-DA5B1uN*MSYP(viticYjLS(=6PP6qVt??csmN{zOY`GuF1HY+O%gLz=&K=BoJg)~~sp1&Z-Np9I z{z^Ue@ne54T#(cm+ojt1EnQ70K|3<0&hM^!y9tompsA~lH|%t432>U6%*g{8+)b_q zWIfF72EKk}?uOjoqpfH!x)S|5dMA1SJ%*l=T5C1S7$0V{s3i`aB#C9o`H9RTkWM6- z)-xLPWejBzH{~#ttPo5bpGaCiCf~oX@jMCJW)GMwUER) zP11jtu8?*%g0nn~K{giQ&zE-?$t;o)*gIJkuz4N6v@8|cFs*OYl&e>a1|7lM1Ia#y zlMW%>%>`bv0G5r`F+ezcI|4;o2O0!&>te~q;YD3dqZoukxGIc&tZ*b|6*~-h!QzH$ zO6%rS7|siY%J#%|iE(P^KE4Cnu^=M!Y(sxeK<2YVx&PDb%&d$I7Do2CIXuZmRnddT@qs_ zkAX^fMhKjJ{E$VFCeuIQWtui;s*%^B6o|^$0mB3=IYQc&$X%=1uB9?i1IPq~fGB@q zk*&Lp(2MexPoeOEOc`9(T;pz4dcqVskabb?IXN3!5)!7Ua$-6#A0?<%Rf`~|DvSh6 ziHyM@AX1J`Q35n+=Tlqx(2;m{Tqk(2Sv!*DYyr1OVR+i2-aij~V_|-&C$jd7Q64(sSVN&JL zgG4d&;@=b7cMvsRhjjvY2W1&0h^ym|`*05~ex+bLn&4{bvYC}+1#T`%iWL)Ll~l*Y zR8?80tO6{`+Z5Vv7Rqagpzb#MGa4E}_XH-XXvwWNksml?qX2`rz2OO}5TeYG3{ zJ(Qs|1b{K}EC*wQg1Et0jdULs45D&Iyt1KkM+AA9ZbB5*F*RkrvN9E`F4R+Tj4~J_ zC!n&a2#u~$W6wnxhjNy$M4ygH)0yv7dNAFn!qxl>(yvVN$y6(ZLdhO{2iKw5YfzSL zP&`_)VDD$ph^ufMz~S0upyGd}ykNS3kxXw_O19x5^lwNmXkY~Se3-A+qrk29c5Ls7 zNv!02{5QG3%>7mF`?-J0{d4Zc+{-d+e^XVZQ>=r{T;#9^x-hhGrs_a331C&A=&~^@ z)rg;lgXNaw^rK# zzN1ijC1gZ-Ac6J()GAz+CQpD-8g%5mN5D?YlQgZfiPTa%4xz6GQO^&2nS7B*RWpKu ztfL6ZnhaJ^0QG1~63ZpA10$KQmzc~ek5Ost|#WI-v@Oj#3dq+x$$sHw&;B1T?CDu|&_ zxf|L=08fCCDBvj}xPcaC=JA|ix+-(2X)C5?2^hqJnP!n5RgdRA*U8KI@x%i4W*1DM zTdraopi>9)OS4j!*+|WUPO*I>B1~6I-?EraxI*wHLNx_QFqLb^$dMrA*t7+MrmK%X zj_|o~L2Syo;aGnR?js7c|C2(%Gch5>W#Qeq@$cI!`UVP4Bt7 zjM3qhUTlV*OaySOYE`NX+4`XwUo8gM84z+!Y=j=6n@4|%9~g0W1>^u!Ffa^T57fl- zT(PvU8pqtQ6>jFWHtbLwzoq9*!`4|GubybOl109#CaTvwF^Ib+(BhWwD8lvHTHfT& zQh}^4ETzW--9F1X5dIfnbFuV1d48JB)nAa4HrXqGbYA)6*~ZGoN+bP1?X%nWy<+zp z8VK{pQ_g$_=I@%_jojH&Ss!#V-0j$|L9H@1Xqn1^LF!?`L1*Z=vb&cgJJg zocL*;AXnt>&V4!eO?WPF%Op+q3FV}kEcSI~-5h_=af3c^$w3-Ex3XwgXEUc>o`&J1 zHHqrd+!-kW6HEuVCxyq-)14ibplFR|nLR*X+Y%D8cUsN1}G*lOQ7;JZuvl>pygFf zBWkG0bQ+E=u7U=kSdHt&>r8+)(mxv!i>ZWS9e{&|WLOC7DPxpQR}0%@YQgGm245jR z0Culj{ZA;(65f!Wc9P)Zy`7|0O4Mp)L8X7?>G_#182Eg!D`}UZJf;TL6G|zp$TgX{ z%sl2>esE+fJYcq5I@OPTg~y_Js^si+00y>(jUDGTB({ppfYWr^HZ}lYDK-E-MY?Hy zohrF zpiWX~E@ArmX1H3;ZUrNlhGPBRC{s1r9HOWbZn6!Td?-bCw1!h9HKE^WI*4?_p9!ah zUOSPqjkC4NWVoR_81>UfMVt8{BA7Ewp_RGuGIw#J1cAk;d5Sh_PL?ACc?IY`+=04& zP(xMR4L99h8SBSGnNoQ|RD=>~{za1xJ{*5i6PyAc&zh>7_-fdZoW(3OZFZ?R<=F&i zx5G_Jp0nl6^XK)zc7OjV8ZCBRl7z3@{voOzj&WrF|^HSek<7I|iZ9Ry_y zB;MPbU#!WsjpPGWD1;DHm}M?=n&j$pgaN$&ppq!B?F9sRvSbWUK0sGqKUX1O$P`AR zG}M}RaM6Gpv-#z!341lwjKNqlYhby1-S9|A3`6Ly0j4|;wU3LuPDyETA%GjoNj=0c ztCVU`h!sHS3KMa8+HavtrIYVI7Jn#0WGXocEV&Yr3d50)U%esu4v|Kkgep9|7HZ+c zwRo|Ph#>oBj`V( zFQ7k0e~O+#e}(=Q{WtV1`X}@gz?Ltk`~_G8wy+0?Ho^rQ<0`Jv$L5hY#Y5@o{`9z6`$#zZ$;|zaC$UZ^Sp@H{rM7)A)A$8^Folitonn#J`2#i|@w| z;0N&s@k97O;7{OB;z#jk@MrNK;>YnH<0tSJ@qflo;-~R5_`l$pgKcO4 z+ua?gFLQe;sJAVRX#qR!$$y3YMsQ?UlRVf)Ai7EFE=-198CRAI_xagXKA**H*wIUa zaZNvfqwu!{O*Wc5Tt}I!ktYo~I^cIh{%$mF1{k<n2?=@N`UyN-UYN|JXLmy+_jUV`Y*2X( z+!3HpRFMlji9ttJuss6J5Dlfil1d@HA!rcRgHz-JrBLH$e}IC1JL+|sP_C|xM?hfYCSPQms0S z4tQ|vVAKSjEPvG|6ypOJ?(rpQoyW&y{_CfunjCj!E~Qvy(whJ(>A4iQdn%=fl|e&Y zCNDB2vHkInfwt1|k2pPr(;MCezy1?ik+&cJ8O49dZvqx_KwOT|n}F!v3uiE3KpXI= zFER?{TUcp~HxBFu0?l^sCFoIHJ$;LO8bTXn4q%6Qe1H6VP^?EWtxjQdewz1-(LJ(N zLMR<_yJ)LSaD^L6Q=_Cy(MTF`92CgL-xSWbJpbV z-?~FC8B*;r!lXlkRAWF|0!p0K3hqRkrCiVE48z6cRkDpK3e42pD zzo=sIAO@TQ$KP~}lLJF5eD zJm@TR@BuJyQ7PQQ7!_EHge}-SB_}SFUQRZbL z(`}x_bUpRZya-SHz-~+rV+1T7&5Y*`?v=T$;~F7~yH3aqA%<%%4_?YU8J1fLy&)~~wr#`ABy z^QzAfGaeggc-w8`KSwXdPfobwm=x2Q_^u47!)58RX2TJ_`%RaBX8f}^8l!SIXh(}A z8vpr~pG6MZf3CLk_1J1`a?kgZL+R{KF3v3G(_GAm->@^tRyva@(^)#0oTvrd54~jg zP1AE7#jq+GRInHB_pBaZQD>voF(Oy7%pE#I(PhFSTQ$36i?lqy>Zq!oofkQepUH)}Vy>E-AWb7Kn~Ny+>*&DxQY563&%P*lXHWur7Cz&lTt}K=p zKm7FXEq>`+*ItXaPXD;QGXA7rI(UdbQufZyn>!yL&mEF$S$49?E#X;zTe`Mvy6A;$ z(zR@LvM6Zx&t{{iF1q$46w;aG{mCnyCCrv z+#4jTld+A8j3*6Jh?lt8I*e()o7fUa~T6<4ix{a~j zy;${H>+SaX{`FRWYu#?CrlGauJeg?#F+k40Mc0*S*Z`e059;(j!cS1n7sLEbFaI6T z1I@A-Bss=YKR%# z)@KBe9EWoI>{|B>KbC9f*5nA66g12I%0f!nicC=|I9y0V45zTkG&;rW+3MIGFxE`RN}Z7D=J zjOI>nNjRe`Bg&cOx$z{A?^)1ScCG}y-%HmyldxTmy{7ExVJ~IsGP5HY^g6$68_+P2 z0}zm|A>oq=K?O(_Jy|6}9#J_{R2S3WPlOk%<{=cm{F$6{-IpRUX+ zIzD=%2Hyrca?|AZ7F{fe^pmJa4S!iE9T>yunoNxWOXLDggI<(=hN6-EPJgi^=$-!N z!|0ZmzdJp1wgThhuvDXvUauzLT_{?)EjLs=aNksPD7?uxpZin z@*)@Pl=F(yRS6^~4gHC`IwG4!ntErt($N@gD%xImuz&`wkzBGkn~s_~rFoRDq zlhsKeEV$WRNJV;s$ULPbHJv&)4va+p=MRpe08CdDmi~_pzMgtkxI%PL2Bw_;rjsE` zCIQ%!NJ=MBcM`_Y2jMvsYh}Y!0YQ1H45cDPlL67#EY(dzv9!DZz5X)#SZu}t@Ed^G z=Cm;G6-W{WhN;QmAD2tl!&}ax*SVvB>Cs)#aw%I$8koj6Ox zIsaisV&J6+11WT{&zSVW!W09{VV2VAYB2A*pjT`F-x{M2TMLc+1lJLU!6+j$ID14P zi}&BsHX^s}zvv^A+nQ@@O>%#8jjm4K)!YQFZsYvpk1_TCuYVq!{xNf{Ik`h!`OfCr zB;2}qYwPIdmxseM@W0pl&M+C4;e}Pq*Gk{Xt^mzvP&iV!v~XSF#f8GSt;8iRmIJZ( zW0xTv1E~X5Du43f4jT^EX?NIHi;bOG3)t*S2N(%Dtl&Q1pe2Sd{d%z&s!bl43*S2x z)J)T|rCAM61%LBp-*fDGKtE=gW-U17H_D!0o)1oiRa4p*e!JSq4t28m{DXY-rL=Qm zc;pQ)dee4#b?g1N9UIT3^`&<#oLXJHWpwBT-IdcFt8($Rw;nq3qSfW&WpZxxqNUYS z3ugKFb6<4i(5=^AT(LT*SGq5-y4zWHIGcQJaXag7cYm$JmtA`3b&+>`?L}_<$5&NN zV=Jxp_jqU#_|A>cH?JtX8S#Ik+}RMu9SSabYrDSRGcgnS})n?0m^nWP;4aw$-rQ=ox8fcc8Fh{|3cuZSd zs+BBYN}>HdyXQn!=`hT5g7Xw{ZuJ|Sm`9v}I%&_5E2`xPp&1O__N8XebfF^Xjc
    ya&7&vw;94NWMGhFxHgnn`g($fr86Xluh_4o~&y$Q-b_&;cFQwn3!a z+b~O==wU6Q+%<~sqY7|XZ@&28g3dV%E`Q>!Yf&X#Ed|o!)j?^oH1A12^(v#oweUdS zzP&i-x61b7nNuCZ6!*Qey&a`4FyT;o4lN50BnD!Qm{68m^EWRsiXLv>=-TS_Ms@S- z^7-b;r9J=mJU_@>;R=PQK5Pca3^CRgS8A0ym&;ZKr`1*dy;IcD9LG-W)}*}zk$<;Z zYHJR+1(Ob(j1xRJ$US=7l21Mxw$cMsYdQzZ*(#-rU)H+_X33dT3PYc2sR(%`rL10A zTmsfE>ke2e&=^m;rk+iHX2+Fsd7kS5Bld-_aQS_m(K}3?EP=4lHWn5So>=mdvY=&@ z9-3(dE=VrBRf+2YH~(88qx3{zI)5gOy6=avdnu)kXSLF)PP0s(EWE4mvBDqaD6VS5 zD4jvjP1%ClQ9@=jgQK1rZfP<@u)rIKdDadL)=6^nd4P-BQ!N5?VC*~EQpyQN2XW;C z3Wg7AQagFV5VR?asfzq;9LG7Nj%WM`fBWwa_t4NhLSQ8ynK$ zOf#k6rt}#Mgt3rPB6LJPWPhdwLvEMXE;(^Y&E_I7tmTEd#z5+bABVB+G?JbP8rJ&e zrG~8|@`t7CwYBG&o=$YG5fdyMI+_C&$_|V(maU7(aCy1m**pf`WkgvW4P70N`nSVi zh78jS0850H8|+tgLqb*LRL%q}Qc+456)rEFR&k}u=TjlZ3lIa!W`9*Y2_2O&+EJxE z8n$uO2=V7(+&aB^;!-8Q_jC({>Z4ab!o*MF%EE}Roj4*Nm4_}{U%%{-d=!tCJNeN^ z9(riELrYk%3!4c=U}s9UKQ%}rM4y6}j(8*bRs=kH=?ruc(}s2Mjg-Ujhe z@22w?{{C+!Jvjv8P+TGMvUK%Z9#aa>mH_+Y|`nQhjf$1knJw2W* z_RkzTGT2rAG0SrIfy6XROCLp^p#bJ+aW zcfL*DIQb2FeDX)+G%Rh(3tFSF+uXKuO*_r-E3MBq_zC)bHAD?{wD_?od0}uS!pOJI7 z+%mZp4uAG`cS(7hEFC7h+m9dK+1}nJ!|g}5u_$BeCxiH{D}@tp(sLIZwPN5{j^zoHYC*iV^MA4zA0CteRY05$v>ue zrhiDk0F+iQ94cH}cwXTHg%1@zQTTM>PYPcsJO*QMkO&Ze%7r6sO)GM@U>8ZpOnA{qjl62%NExeoGqZPBsfa%?$^YZx%fGl4sx=!>K&)V;|^38{wV z@}Aic z&5_PUp6(*JjCWCoRH}}@01BDV!2q#!?UXknl&BVHTW>4G5J=Pv0iYD6AF6V8Q+iEKT_Y ztRSj_iu~{Qj;MGqL(Oys3Puh#v;gGy+D`n1qY-sl#6&$Qe-ck)ZYc+D@R zxBbs9rCPUnz|899ouy5K`REkB*p?169qw4ta_(p@u!l^~4_)egw}0C_D1Y%%Q1V?} zU3zC@|5E5KN3COCpg#dVzEL<>I0f9~7U;Rx7VZH}{r3z1rtrBuV*BTkOVmg+H!1VX zuZT)H93$)p%=j@*|8D*rdS)17*4(t%-|Aui4Hc?17s|6yE%yz&!>rwHCwLse!jd}f zT#ULy^J&33EVW7TlS8PIjciGYDy(p72Pz zF4v=%r!MYtw-}c*HMcgH53>HHpoiXDct1#+#|lpt zzE${s;r}lDkHSw1Kf_(Bcppkfk;*z9&%6Y4N5dTbZW?tn-TYPl(MnoOG2FX7TuZ`R z2Kl;o9?Og9Cp1@8y9iOP3^+7~kudvH^+Mgz8L1$?@qZp>Vv_3UfvC*NoRZ;D`pnxl zUmsT`&6dpfbr%qZxT^+VqP8MA)jE-M#5n7Wft1JUXhuK9K}nq}E~JQu>{T~wglAE? zT%?IV=eN*pjtH?*EtkU-47gskQBUVUhoaTK6(vbzm&3+qbx>Ix38cuphGq?`Wycn- zWK6oY34it+kKP^0>VbNa_+^j05tKm~Y}+n3-AXyg;>pK)RZ3~tC_719Y3K^4<{%SP zYKdDec@^Q;<(bLj1OsfiRxcG}J2k=vXA5s}%4xADG2(<2mm_rl7s3=}AbjWK30~&H zf2C#w1_qRL4rr7A25;iqaNOY~axc@599g8gWq+1DQMN7Jmf6m6+p){>Rt&Uhv*Ssp z0*utRid1NsA4|Pk@0mqM$g<%y7n+XY0DThb^s1UoYg;CR4yibn<(7*+?mm`!iG3nS zgRUP~;yu+KW4$_eJQ*zeUMM@dpro9|W?;yu8kN0<4l}PFRAV2wr!Px9$)x#r+^RZh zF@FsOcV$tOGk!b>1L18Y6(6i?CUnU``y)f6YKW5avjTit(VB3p!B8ANY8GF3R64c3 z({Vuknc*9-HIQ$JwzhzlUC_h~5*T1R&UI}8uNB%=yG`g=D;}nIUbyec^(c;`-}=)x zy!_>NeW7`2dt>LNn}Zd$cHzFKFOS+$uYb3;Mb>)9j{RtLah&Yg6e;NUc3~CgnY*91 zyb~0Ks}ic=qAFFWj=^BK!(a}vw0YoI?it^je3B3u+lD9S=S(k&QOl7*F|eBS?TrJi z#(_)l!s{j%PtT%Dh`Qdak*k|_7zUE;<>R4GfbM@Bvki3e$vyQc&-bY^l_@OO2Y-80 zyL71Ha1}b#RF;4UBF&x(lq7rL$jBCkD4}C?Or#H<1J(osnwkNk^g1$#q*rn@(0xEV zbP#iv36&a$!t|&c&9t&&M-ERcTPGk;f%4hpYr<(4%XTqt7Q1u&@bkYVy;c9h$H205 zOY>Kptx--5fvzx6uSzrUEC-*;B!5200vZLonAdovFt>lFu)SqXl3&cf~)_F83LUt74Y?bT^g5U#FCt^%ljG4SKF=?I? z1@A{!nf{;ymyM1o@Il8RY-HJe$~mkE;$xE{_y@ZzSi zPO7E`;{ZRwbcW6-gRpiaX3o*d3FGrf5~z2|2zyVB#D9KmeqkIm7zRaS4D4*+R6d8Y z)Zm~isI5{$QdI>FBQqiingQHYeKtivC9rh~BFbWY2>O1qGE@q;*CXKO7Dcj9o{{iN z+|0}n)3bOc2F{{4Y`}0(fo46n1ezVTvRL<7WO&xH3x&c!@-P#VZ!#n0p$Xc=A^}^N zm^M(DSbsvz9LWzj^^&hi`MnVOQ|0?5)US6ay?+HGMS#dWrbft~{CE7c%sF_oU^w3= zEYr6>hNnDhDsO&3KT^0k&tVL_0QYTsW5sWZz_)P6cDhP4wPBG(x0OxTR=h=7cM2!X zBU2=`E~Xa5^z91JK;;GPC;ct*=0?oXTijv5&wsUAG;OQb4_Wzm3!CgL=Sq4W~EQwlR9{+PV-<=*DH! z5_aSROMvMjKy?wO;p(n8XSmppnhc#tf(q^6&K#k(thIZTO?8#;u(a@k!tHsk%6u0I zxqs^JOzYUi+*h+L((%+NnV%tvA;3kxIXcDgn_0ukB@rZ=x(8jYhSQ_@Lufzjj6=^F zE`S-&qpBI%G6@Wd^G92rFUwfi-qMMikMD>&U#Qr{adu_}5%-9S(`{_6)E71lV_|;n z+;cX{(gCHlY;S8CwVjPcAj7vV&j&j7y?=IWK^J_F)ODHB2qS+#56G~9PPQOeni$2}Z@PJ=Sk+C?hZu%}VxyVF(+fr^Thr;}mVc!* zCa>?j`U9sfe~If?Dz7+u@mua_iSSE790nM8|M6A0VB2-cH{4V#`Qc?(NA>z^&R%u; z^By}eQD&TAg#n|J4Vuk3I8O9$r}pSx}D z#7-dX$tOW7L?@zVue8(z)?mQYPZyfU-K!R@kU4iV=IVyUfG6;~nXTzU7bY+j@!`9< zg?Z>k=u+cnhv7af-Qzcp8kJK28ZflXSVfz^2TCie_zdw*cFFmu zJUYM0&#j%k@$AMrysgtlxl$TDrw6v`v75$A(PkIj=ih_y4h}Da7#H`g>D;~SmUT|- zb-d{Ycsn!8BS^mwwoOuKsDIVFiwkE8=T!z?g!n>$Q$&apDsp-VN@1rtb4nJ^zCc$r zHf+KL?Koe<%k-bSVE^#mv&hRT8Kqet-#%`ei;JdBtCO#go@v`}E$^M0TxOe?dkxRx zHTLA|>b?PR>kcu#0w?SXPuk{v_rVq4Z&h|G%_h9Hu8K^1$Ai4|58?H``sAuEXs^xBwSYyuC(}eS8(v4%PO^ zbQK>ro$?4N)dizxSsJ5R_PrcFDK|$HPU=Vn1#jix#xo0Vogm)KG(d~z;)MKr&(#du z6eg-Ss0m0RUC#KhcyfpO#jY}o%?K2) z$I&ma6>hxqvVT+mnMeF^WreJ&I~IeGxn0+o+(9(bN<0mIF>;+-&m5M0vDSGC|zE8^`t4O<==5LTA^xhG{8+jx5FFRaa z>K|AbdnqZ$&mT9MY3FFqGa37iVL2}9;?j(OZQjYH^i5R8B%Sqr#O6RYoKe|A_ag-M z1b!~vdwUK{=mIj`uFOUL2AR69DanEHFw^}seP$!kr{*nBy4S31^m?y(zo{QN{OVu1 zDdGltEq~E8zOmGbS3}^SEL!ZBmzq1rY`cuvcD2sgv{CF9FI_Cvh-=N4jJol{i!Yyi zN;-b%So8;1ZJ)a2SFUKn;Ax+G)pZwN9lMwMR1nc440Q)bn5wj zv+Eq}n3$nJZ-j?;%c+CTqUF{8zw#s7OO)B4^MBo#N$4S|{5PA0(H&%906Jl4M&s`0yhA)0y3*6Y29-(K&$h~|wo6gN62-~G z{xYyC5bp2~j9QT5admkRI$>ag#ll?Efq#KbIoLqnI((5X_{1c+x_Kc#oaud z?X;ThZtL>#Xs%?$GJVJ4lakVgrhj)BnFDJx3=GpI9P}^0wGbC|*$k(#}klxJ(7|A~2p8s^nd>ED#QWTLc5SIN%NI8#ouX%5cj(pj|0Xd0ZP zXba`%Y?C2TnZ3?4Q6?%41 zQNKPF9ch^6JwYs_rM@1}G&9l>=KVL}TWINN(L?MnX@v*t(9(PTpSv{V|IDTI1J{&T zw^P~OEpH8Lz2Ln1b%KbOGQHpY@l3CiMg;hXRbFuxrWw@@;# zl5+C@=VHO5tgtt)E3Bq+J%0jcLSQ_MX+rP6aWiO=TR*BA?xSz6 zU!GRhPM%wAuirAcZ{J>7E?^F(K{lK!GRZ8D)--X}P$@tI%&L)f=UF<1nNVOY$^|(m z``r6*s&N%_q!wJ$s}fwJ+_w*L&IUIayM+>snRg6mvTYQRbQEYsWLHVR|(a>^;z|j%^8FPZYpJCK=O#vxcD? zfr%-OUN%@}GLzTsfMyappmL>kunU(DU9_p|ZnbDs4JguQylh1}eJ8GPM7qVL;n-9X zJ<`E)DNC~@*-Fx|mwymLN*J71-MXPCP%oQ-IYjjCq2*;BnX&1IFk`sgJK@cHqjgrb zMcHN>2h7AXF)m?61M}Tr+@Mv-ecji3*D>^?F)<#3+Ij_uz$%Bl%FVLo>%8iKy|%Z% ze*>QF@pdq_E^5ar?f@ND`rWqZ=S*#zMESuQfzznbD1;`p+}oX|3m&&Inqi!(Ctz9@ zHC@rJi2EQE@jjJC8|E{v)c$z&-X+j$q}Jh2%MR0YQ)*5&)AxDn8-MZ+4|L~~JqEnW_HXuCwq)MvyE?o0fHXb-7V_BQ;+HhrJ8`F9lOS3q zk6lYm-@nWEO?uL_&C)GPk1t(4SG@iB@m7awFFU~m)0aBhu(!HO^qbF_va_EX)Gfq? ztZ+rXXOXI~Gi2z~$g^<}7xQ3_b<)P*j;T=rM7?J-Cx4i6V#>d8jXE2Vr$5s=U5cFlW8Zoyh~) zL7g31BR4M|5R>nHCUPXX(Kb!{z!_jrS^}g~-$>BWoLQK*E?!&dmqHrdCYW@d)jo_! zJBd4WKYt%or$!70F-0qws+f;;HrSzR`E00mhFzNH<)sQD3DtrL`Jq=VdXpl&lf^$f zK0fbtmO3@x49mcr9Ya{YSxOAKIJCv%x&>XsuMp?_(v+LC`lF~|qme1SvDDquTr6^<6J z0^9i}(1BiAxU2Bq!iNfZcn@=fv& zRM0JYF}+z~q`Z^9i@uNk5q*$;k^U$8B>g7+HvI{`0NO%{)mV#l*@zuxN7)H>ie1BA z$A8|=-o<{0J-|M|KEgiE{+K<;{+#_a`zHG~dq$hrTH2C!nRZ%xf%Y=(_1az9Z)$JX zKB0YD`?uPkXn(FfrTwk;UER{x^)vc8{k;CK^!MqX&_AtzQU8km4gFjCcl94}26jw? zFYqOP89&XR$8Y4n%-_WC}h@d4xC z7@s!&)c6m^6UH};|6+XG_`dNE#*dAO1feO*vLnaxh`d~0DW4~Ak+;b^~c@u+`7PJ zNrk_R(+RU|t7wYuaNJcf8u$~HhW0oCrkBAJQxC!D(eX|c`^h+uXhprDH%7E{4?(=Z zaKvPIO0htVBx~=|9@ZsAZ;MtJFMr2-(p-%j;s)+O9ZL~yY^3ZWOeVp_V7l*xzQZ-b z^KHO(3(ky`3bQ#Jj73p^afJJVt!yA-Y)db}JIA?grD}oe%K0AEsF=jPK3u+)jj`w$ z9z{o3VPHR*KubBDKCjzCY(;@LV%>n*WN;M2pVJsaDB2@9ixuA-^s!@v3V(b|x}zjt z%!97Oh(y$4Q^b^=W>Xzy>Ym}W_n?WipYS4vzFuBHw~6tI z@U(P1K-D=;1mb!Dcg5JrmVd(5nF8*j6F1UHQM3^#3-1|?yS-Sn`(xBM;nhV)s}Y($ zSvtu2qKIe?X$H-~oxu^5mm_u|tP$M5c?6EOc{WN%=@y;>3wVrRzWA&d^J4(vJnyWK<_@ zXar_q9*(++s@`BYj@!_`aWO_P0{D&JQ;bCGA@RBBrqs$i8y7QkYaZ?&_eY>`LG}7+ zCQu+{2;G^bNSjrH&jJ-a9`~V*-L0Y+4%+<~1{5kb!U=}ub~iz~jWPHDQ!6P~Vs&HO?&5GM${DM4m0cJOG^+YYS1KSbLyAmeq@yl$!&nUw+}afgbF7+q znDn8H6uQ0}54vigu#Yji1^0ttnhyuc%79koT@0i!j<>ov!+#ML8DUO)8) z*@Vl~stce^IFqv>jwS{LjulRbBGWPE0U3i7#z7hNaUNhO8oCbvVe)o1OhF65a^Ys2 ztL=kiC&$L#z<(#VfbhC0l1Lq45Yi%UtdH{MkGFE)1?-m5Fxep)ff{M!M+={ z=jjpDHq~?k03Dw6zNc1{`TA3XgLWB7YsJr(zv5Os0-*nZsT(?&n;u z6{iCvFPsN>Q5U^66BtwFi-IOiAD&-Kkp;jIpa{VSVqJ1l*@6i$Kna7`0$VV?9>r11 zR3C3a7hnYtgCCgi7{@@to^F0iz6ZcYuDU z8OFb+n`ysQN3RSx*As}GqKH_u6u)kFF~AZS!+)43BOLj$Qk&adAWfV>0-jey!b#f* zE7wE$3cLc!Xpa&O-7-K*1&O0%OWrNOJYay+JS#yFyArqqMo`sJ#ARt8CE-}W{D?u< zQ2F1$YK3-=VI+$ER19Fq5$+AyZK^md6b_uFh!Co5p`h6s0g3R9 z+`AfPK^tT#j2F;{+Ofj)=J*U~Os3SCRA4=!nr)!<@iy31w2yt2_K{=Yh$@=tW5zq+ z06^5mw53S3zp3(sV~TLpEHEu_A2}zKs(+?%w-2-jY+?(f)|RsTu{kZA6i8RdU^lnW zeQ5}kfncdXwn*18@AmpwA5iKrz-Ftb(0#>_-bJWkHA2^6xJ>A{hQ@VW@KJk5x zN{+aSj2J%E*fP;L;&z*!f&sT3rsHk`W`o70-CNPG;klY_X}W$h!cF}W(WovB>3?W0 z`b`i6ZnMzqgBp@6$SKJYbxOmC2`~mVTz(@Ch5-_t%QjqL`w0DosoyAN=&Pajp5ZA> z9W$uuz=9ibFM}TzJP04a6eu2N z?xq6XBD$uIqSp)0>xNG;)n1o2y@F%+vFS-Rg@426C0p;D$TD@CS_4(%JOwht z&S8(z{1nNN|GoxxBzX^|i+}i(Me=Sq_W5Z?{i&)myxzIBAI_&AECeF^KH zBM9d3=ATi$M3H(K$@>{ScyO2yl7eGF_Y?B=yxB-a>Y0l2p;^b_%Mq%aLv&SrrnC1_ zIvYHden9P1Vvj;CK1VsK9?rG>UM>(<&LNPtc*$L<-2gKvYMd%bbp|?>I}f z9%&F_qSFl1r@arAHZcjEmWM_`wK0MTCp?t!pLGcr@jpi*RF%SRWbA{4dehynG6nGqcP&E#YYS5MUV1_kp zvu0>{8}KF^+$}<$KoWxI{!92-n$8yu8l`$1$yi}CYC5NXjL#ENjW3}BChEJUPa~vC z$X&?s6pf_F2^E!~FA;Cj92IdoQHY?g0%oqJ7Bz7{Mq*b1j%vv$P@(=-u^v$ ziXp6nj5l$Hu|5`Sh7gaTL~o;r*rQ^dSlhLJe{meS^_w4(wc_Q2Y!;VRZ6Y z&5#Mmhw}bFk3z&opdwVn|0${lUU{BD>8EG>L{Tt>J~<6VeIAoPu$*h^I0C9LW}*l4 z@8si*gs`aYCx0YVmBwrWUtnA_nTrULj6a3MrkI}~E6UN0kPq!)1+o_tW*|Ad0~<@l zTQE613KzqdI7%E?uh7|FV11##B0{bM`Jm%?zntVfL@n}4qU*nk;INbs$d2YJ%47j)g~6up)E}Le)PBdEog}* zs3|o)#r*JseO}E|N*o)f7_{kriGt#7gfwwhJP*Sm)O_nGNKUe>@Drh0NO*9|CRP&b z4$soJP?}N4#l9Ar*v0YE1@5~-SF#m`vYP8LoWdB67Q;Nl=sXETY2mEabsLAq)LD)A zFqW=+9ioJR)_g&P8$A~ac$YKf( z;ezDX=1LS3InW{|dGH)y^u$bj8;Ak=L}n3GNUJw60fV2!__z#N5T<2;r-(R3B7nxr zS|CFYDg`sTzXT+zD1qB{R7Bou#kTa-T+}2r;eQ#L;sk;QHPOHs>=Z4R**FtSW(1lS zN|>^6Nxf3CH6tf?;drhVVJaP*u7V1i+RSr=hHO&fKt)K%Osc-Ikkdo=+R*>9NPrq$ zbcoe)@)DBsPUH^~NuH5KAakLE2H3Oks zq%?H{X-HxLoTM5mVM^RtpkL^&(0Pd=A%ZUD*nZPD0^%E;%rWv#0&+OWgWQFMweCEQh(eHZuY#(Ky+wo{&P?_Tii3Dfd-5(deK71 zmvl`6JK;#GOr5Tp;oT6I%tTIl>toOwj;_gr(ng~MRu2%*-dnaGTQvkM?1kK#tQ zW(gZODsmI3w25xm%rs)ExrT6oF~d_)(ikWyHFV)soY*b0sVwps%teX-ujulPNq>$w zsIZ2rvl2);2X>%CM^cqC&9E?QiDp?uVUgoJ5fd(AK$0a3xCdb_>dBaTnddA% zXKE4#$&QQw3ZPaQ2E{>?5Ev7LM$@1&mQe($1~Waf(ftZj0x{*TKuiZnmw&(!%-JNA zC{v812z>{PL|5_)k->D4OGFqW3KO^pl|NtZRKFrYW7N}7Y3Gb$X)J0HC*EEEHVlJ3B9 zF*^V}2sjjJ6b&nY0WKfT}4hUFq{Mk93yTSLx13dEHt4d9~^ttOo1po~#NrLZzlZd-|m+L8n0f-q3b2G*`?9_GK%=jH;S8eN$d z0#p!<$lQqYa8^URK}{n*T#Qo+nYBP!gE@s645y8l)dc1SIzeN^6o1GfqGmZ)^BQI# z$XQnyKo!951u9KYMO_6YC|o-kLBrD+ON9gQD2j>4Y|siFuFM#8vm)3KO##VojO93qpe#f4YW`V)W~VB zuH}mI%zT*r%-^UswJ=aYx6IP?qCBmWX9yb_jw!0+R#oWaz1E*3ioT1N6F5?gWt4#kO0hJQMIjwQvvazwLocd1UI`mBVAqQw$1I zUQE3`kH)=_<-YR2#DIWV+#+^BaSz~RJjyr9F`L*lTz^1e0HQ|}9NEaxiE;-+T-&FW z#mt)nDFVb|^KdaW{F=iMt)7axc7>XL*bEQ|k~%BdMkjmivRlbk&X#RIT+)wpve*|3 zRu~pzFV^{Lq8TC(CU9-DgWz?q2SV0EYffje>r4Auu7iMz%C642*mNw=k*Ij$iZECu^@y3m-=NF+u*PU!drdC?rvLn+2o6(I< zuFXqL2s6y;Z4*SWJmDE|JCjK(@rq86-eTG=HftraLJrD+11gv_XSC%pw-6QP5e0na zP^x-TMQ5;}ze_eT&+~vhed?4#%DQ>QOJcIM<8 z7Bc+2;x`sD^7!Ome~jIEfNHaHevjFCE^89CMYr9wNE>wJ^S^)YR4;XD){0G!U_#4!a^qW7dhq8Uf>= zJFJuOP;|$sp#Sxm2kyLavUA~qSKN4xun&_*Cl@~a$O8|QA9&zn?|eZmcF)df8pbW$A1cc zS9k_}H>d8GGbq|V&e3%urjR!1kA)k0Gao9w)mGq=LV3IQbG#ZPsVr^HVm&Z})egl- zP|Oyhb{OJ4jNE<#6gWh_rYn%WIQI#|_t7+~_!NPln{nkbrW}K@bX*u#;m(ThC2Ffl z!3?CE2Qho&JjL`!Qy2T(xjuhV5r3Dw_rQ5aDuELD2T&>2lMZH25Wbd@ah2-#G97~k znf8*z#q|`^sA{bUSjJG(oHLPMMKui6C(|;jdb1olv?M_bV{yb|(raJ7UPE55FO?QA zd^KOcb7WWoeNnP>w%37dQG(jD*+b$(N^vfsG5Sq&Z--S|7 zgkG6ku4@hpK`GFkWy`REK}4YK=qy3yCv-NAgBdW&aLYDh-FS{?7LQ~*-4g1Oy}9)o zbOEQH#jKkM+gU7|xW-3A4k{;0=Vgb`J*ZjGLzze~oc}EzIqOAs{L;0q(-|b1I<^ z?j!B6T{6I3nHKL9w||C#0_~k88&Ym`qiJ9W9Q&V~QdJuF*qUp7cLlUdYt>qbR?Elh zHnV$WOfgvBkV%!$?;z49Ba@F-ad`+mQF)}YRKZOw!WH^5SZSdHgWX|hHen(yR>zd~ zl46uFp=BQxyrVyO@H1!54D<~VJ)fVH*It7KjAGmO-a z)rO-2_eSi3-XO>R9gK6nNHGOlf1Z%IJq(6@AmV;JgzvcJ#?~~``;ne#)<&sU#zymm zLyyj_*`?4h+JUu^ZaD0Jm_9B2_?-H?_S$pjemwbignwEaR-?mw4jhb>BN)>vTB+PI zH_SLPSp-jF5kgp_X=b<%T0C_O!fD@}|Ix=L*Mi3D@8IN@EFV17w7(2D<7yBXIyj_s z+X0r74I}uMz^k(j{eC{-JD81!(BUd*-6k4}XTk`-j6f4u|g<4)@|_zCb@;a0*q#p(m(5iZ()Xv@_CHK~!p5^i(Qaj4v4V z60&joP0zoLUBy z&fLsZh$QB5$Io0{&65g$#o-(6YKn&n!i&7;l= z#I(gqP@)YIBLrQkqO`RPI6@zFw(i&?%G0`{Oj^BU-{k1m#%!~%75DJ$DHqiM)yl!CcpC$?c`lwJOmdl zWyj)M=)>1uH>*U5mt?RGvO`f@alx3sq;b7?ndG(U(!Y(n@$q(kgPZbZsOT1GrZmPYzVhDYv5 zhDjhv+DZH@O5RHXON>kWOlVq^TJl@MTlQQOTu@woT*O{HUSeNXU@~BGVIpBxVYXsm Ivn-4#1@cJnssI20 delta 33601 zcmV)5K*_(zq5{;S0u*;oMn(Vu00000kyHQ+00000+`N$#Uw?c60L180exbH#Wnp9h z0E%n?001rk001^0n-;2QXk}pl0E(Od000~S001NhyaiHdZFG150E)N(001oj00M-0 zQvd*LZ)0Hq0E*}U00Be*00BfID^u2OVR&!=0E{dE0018V001BYdI}J3VQpmq0E|ok z00ABV00HLSN(vdwaBp*T0050_vHbc2e_UsECs^Np_oePuxAv<0s_H$xz1Al^={@UN zyKKp_B}?7}0)xN{wqqy61~~>B3;_qzj-5c8Wz3kK5Q7bA2n5HZNqcZ&HzX_x9TEZ! z9TOAWfu9qCdt5W;ySJWXW?<$ws#jIF?)oj~JLmsD=X{@XGzb5PfuG44xngd6e{O#+ zhnfqhzlOS16WSIO=q}m zn^2fQ$PyeK>t6cWOM5-|=%V@Nf9melq}I5jS^Xx)gi=Px%_lXD@HeAXM1<=2TYXm( z^z;xR%!J``0@cH59T8c|9nake_2Yposf7feMa@OrPMU(-hg}pA+t(&!hh2WA3^20#>N|quLWG%o&GU$(nXI~*aIiJ`*f3umPd4^fG zh3%=a2_L4TdbYlCW*k&Fp8=B#@&)0;2XK_{IgmS&X z+j&tiLnvFLVp|A(DXuoEe-@{h*rBQE!iY_k3QTfHwjur>q>4(4F7=-i{rYUHJ!XM`4;`a7|Gh z&+)5vUYRnu>T^X?yn5&qMV^26!yo?cBM*M?6O%g3v`52kDl-ApuH$bW?ZY6EM+fpWd52K=Ae9U0MbI8= zC>?1bdWkChWS5Y;9A+AK@_W_1AO<05j?#i(rIx8OhBUOOddCez1ZvwZP>hZasBLJJ zQk1CH4(M|bJFiTR*QuLl!r|1g7`1$A>6oD$r$=TmvOi^*fAow%8d1l4T}FQFsasdN zO=vG_GI-|3!QZ;U)irHt%gTC)5kGlIFqN4gAZT&@)KQZw3Sp)CjwRDrn^T$Qs)crG zRDf?Pg0@nDrcB$@A{XRt$h|f9F=)-v5C&R0=w!PEU66qQ`dY{V7Ipk2h?;ODfJO-t z5k$do9fv^=e||~H@FRH<3_>{E3wrW8IJY+%!a$XJs5=~i%nXKEKP;j?3|cwZlgSCt zCw!FkS2}9fQD5F@*po+kgC2bPqB%$co^bs6Z3XJIt0%6$XsUrIq6~D9;hTcNh$Ehk zK%>D1b#;~Y`&eL1gW|#0U=UAC85JfLyr_qvOXq@of6LZ!*lQg8MNnxs?=@hz9h~=L7*sv+ZrfD z%rWs)q)-ld-8LLA*njC>I4>6t1(m_ySiy$20h%GI=H9is>=OK zHFGK=f8x}YS_xB4SLdII952GuvpVEPrXo&>W-wbA%+!UssK{vC!{7NGmha<4*iK~sKUunIFh?+(lQQ4eYnA3 ze<+oR9O0ubSQZdEc?t{#IJAH|V3`&OjD#>)LvV*0T9Ue=m&z{4yWjy_JV@}E&zFNcVx54=8M8!)D5Amog_?^Ydf)^iXiFIB0^32?Ln9p zI4e^6>OrNVUI?SB16|T?azE@q6(f1ie@>e?1Z>ilqqEcp&E#N$DGtkR1C&lIqV1)M zMI282<$N4Qr`DDtnF`5+y@dPa7c5>0 zK!gh+Hh#y85y(53ctwsYuzGCPY!(Zg2#NtuV8v0WA#^jaG|M4J4NB&xz=9H+f0RR~ z48ue*U=V0!*mW{&))yP~|1I`b7(Cur1)WBZ(_K;ukJ&JYWfJxTiK2kRrCa1{M2v%WuvT7;TW{aHL9>|AmpFvFr zWno>|x_s%>ajRr##KbDp3Ny=Lf0{-aQ&j|KUO~dvUdh3TmcVAf$y_x+1Aq;H zZJ|@`WOJd>SgL=4gRT(@rUo{Ns$s=5v}6&pE%g=9eV|h?fl`9;3DsCLe^jngD5JQ> zE$97$w*vJLSXG%WhgD`nFTjt%s26}A5*SlKV*n5$Az;*5UkZBETL6=qf=y7`AeGoe z>o2|M<%2|uHvnW@Z8)Zjhc@gZ(vA~Y=-)%MjqPt)>;e;U5$SarK#*E-dM z2bTpAYStytP-`vK#zoSF-BmE0w8Uy#?)fQg{xaS@Fs zO%#fDM~ncfv?r!je>9;oK?r6t)gge7ni2sv6s=nH8>Hj)t-t-kr-V3s6@-Qw91!ZLr%eEtjBnUfWAdA#2Y~c#FRvFIgny)&# z%f$KU8;wg3IZw~U-$>B=Vs$+(lnV{7pwB$OIwxXkYzU3zf6e9gyk>|Mf^iI($&A4~ zA~4cbzeV}_PivMZ?I^&Og88$vHfjm~x7?Q8j@+eEYLiWZuz(~zNUeYbt+I|l1|~cL zP9RBYGk`>-bU|wJR!TcKv<#^3s#NPyPu>B&(CQbrG~-zyK$`7OpSMRjJafaV%z__> zTA*P;9ylcUf7;1))32BF_5957*9nHUqI}K4LVOpBtk39`TdIcPsTNL7fkxH2M+^|l zi}Q}JM-1^gHFdjwsAfVlVBGe|_&eOAs*_Ll#XvPeCayl=S{}y;tq^j8XhbOsKp#f# zEPvWUw&a3bW)B;(bKR;mca78-x_tsjz0quUTtK_MKL!Y)OWM(%@l_!|4_G9Q_M(TF zM>o{HrD*)S#e|}|<)bSKO^Uz0p_0d992tM|l9&ZgmCIIM^}IK-Y&fJJHvWs;42*{NNt1yPgcpxT zyiB}$0whwk!5YpSTqxdRS%EseACnvrC;`}$MG-*(wUdVtBY(sY!U!ZffXfY?i*tP` zG3Ow+C--W}W3%pomk+}iT2wZwWCId^p|l6!HEc^*HbFw&A*tduoE}S0kK`c`y1}q2 zWkdc+*8wny3ZUQbx0SN8ve*u0R9QKDeNc@mWuQ_sJYqX8Z#s$&0~n52jmoGyV9sBm zo!8xQ{Tjj>!+)(i*RHA7e+IB+YfM1zshIVeb|P$(>BP*gX${H)CH0ZzDN(k z1!<$AA8G|kqLNcn4P8B6X*MfV<8EGqiaB}fnU>j_Sw6Dl+GQ=AZM5>_;9D--y|#UN z%anTW?pxc*Y!JEz=BSk{U%WVoSL+oJV1zDQ-#|a`mZ~Ce0$B0u#Xkto+rJ*Vp;ZT> zW02X1!GFLY`K61O+Ea}ZDU`RG`3v82L8CTOIePNEQOLmjKoQ{Cd$yIMEyC2;g>PxK zH}&J!@E?O6u4Nj6gYFj+8ZS=d22_$nexf5f_-lw91Q_?h_j850tyrk!w$n_o=k#dd z)5jj(b_~4G1+@!!5z2()6eiv;G=YKwa@drj1pR?5o*`0IYnajC#>0Lp9 zp8(%Pqbgd%0H<8&h&3#m*uvx&9s|*rw1s&Q!D~Bj7>Hm|M2|61xbGAD($1dAHGk&yp?DCEL zRD1rqP7wVgDy)rva8A!(2jlb=vQC|LO4>7a)yothP!lOMz<@IHC7pNe^hnm+ok2gz z95Fn*Si+J8N$z0;uc3*@K>!0n7c<3Iw0}iE@tS%xcm0`L_O?9{cuuXLOV1Y)RAmKb zn}XIhSjT8)AtFdM_l1(VE5Z=Yw9PEH-d^bRy+J%vZfd5-s`GV05z?)ES$}9C z#u%No#|4tlJ&=1y+T%q8oUIS7u?8k^0okZIXv+;r(S{igNgJY>vb~Nx!vap}Cz%^G zJuY{pR%l|KrO>Y8cY6`c0?j}u<7G6qMR(P|xb+b_Lc@BGyfxoQec0n)y|9|2yK@#+& z|7wto2IqbNl-@C)tfaKXRdwGCy#Q#7GYz!G++CS&EzW_huuNfwXgJsH!r$iytE+?U zn694O;X7?fQwZUzo(zGe_<2>&A8z=0+iKVat2TGHZ}&Ea=L@u2&@l) zBuJghT5g9dqJ}^({I3tA{eNix!!KTXDLQc}x$zaR7~l286X?ya7+-Y%{r6AmpIvL_ zyq8_w8G#n_pcO`)vp4z9CqFs!*0&!1u4gT#sv~lLl znI{e(2F{e>3w^?KZ76q?J_{JH=Qdmhw;xR&^NYT}g}dYF4}bVuzvz!>FMS=)-8?fh zGuV7jPfzD~CMSdeTWjT}!44eE9hcIQLgPqej;R!ePQ)aYkB+QnF9K`SOJ%5+Dn;0~ zWb|hTT1DL|2CFfPMt=bu!+o$Ah)lO`UEkf`J>O~+LS4hfs90}pfgR_)xjlpDV{KN8 z=VId6|1$)Yr3#IPENJ-Z4Kuc5Dj+N7mdMdHPN}9@nC@R&T~n07p(?_mpENGqaDB#- z!>fk2w&OfntG)e5w|iv2%T!Gm{=vPaf^9~XeZnbC6>V|D34fyGtG0eNz61UGd``>d zq3%AQHy1Dl(1L!tHN@Zk!0&$OQ|~_5Zk>5(eR%0H^y2MrMK5eruh{;P@n?^m!-i#2 zf;-5T%%z$%X~IYj{2GGMBYB<-e6^DXk~(zYB2s$utCf;ous&{FsRZBp+3E4ubzcd^ z9;~Y4AEM*W-hZNVbRF)z>D8*C9Or-H8r9%ir_o;lvD$;i^;>i|K&|m_pL;$9o7V!Q zby;TjvKJVuVK39EDR4SVGyz~lpsSPAxFu)Lmiz%aJ4$0G>9N!pWCuW50QOtl)-^&y zn2c&ouYX?q!q;B{8bFQBxos6|^i!QVRr6PW7k7(W8h>U%TRSkg?6^aT8GbRIOa9y5u{5zkhh7JqvVpUBw)O{qQ^h3P9m8Q2K%k z_Ry*b!ga-{MZkop5&>KD6idZMDFzGFDhsZLa?= z#Z!mBtoT5FlG$KSnL1WDXXblbO@l^ZU>KAw50_Slm+)4ltl(D!T7?CgqqaBlGo4x` zKkTjTTpA{xGu^s4!n9i6GtqHl{8iBSmW-nYA__W83_vgk(Q{8d@W4~pf9iejdw&Xt zpF45qohLp=;EUXO#b?jz$1jt^(8JTxqqiBx7a6S1ggPrU^d(8vM+ek$1=^qnrd>BF zZ&#Pvmvs8coMUGqW^fM@$=oQR0WoB@8cRB`K5`Fgdrc%YXBWOA`Uo;b|}^jRpgN?TE}nGU!VjB;!HRxgsA| z>|a`7xVx^cAFeG{oP4`p97I;RS}LD^xG`0opIVuB-g~w>S1I-{t4w2dzJYcPYg4`< zLOU)8-T_na^H&;kju~tz+4lHdn^n!64pU?bYRq!VWTb_;oQfKqXwX6~a(^3=6QZcm zaYqgG!uY#*?S?zXUx4pdj(>9dd*1V&@n779>TkPq{380^_`CQ1#{XGIwU@q#b|UM; z@45FqXz6Wl8-MrCbM|8%{}9lUn+tNeQ3l9^RGqa6;6E3T;Nz8TC!#B&GwX}}_%GkS zw)W!UmG7FmZ;Cv1>fpt*1bXGJ2*O_4|LCq8$xW+4<+iw}D~V`(YUy`@Kz|wC(u#!{2e@Gj zOoVSy+v=t0TN}G;q&@fa%xq|Fwbsg1bKZ&=SITyxAWV|AnzzqG_#<%1!pr?Zm9%%{ zs6uj7kx|e>K_r52A@$C?Pn`l2{iD0@#van%e%IUIj*L@JTm_qVzy0oW`e2^ykmwI6 zlK@OL>Wt6}r@r}t_J0?@*naS@$@V8}4}A0LtH1d`ZPT{l7sv@1$I{1MmOhn^Ob`f| zC-tHyO}lIek9Yv#@UFcZO_3MlpML4DRhR7k@66P7oqg|1U%CvN0$;lEwhMLrtN)7L z)YP~+zTuq&1M4JjM{gRx4R0}tan4uptHjIQpZj?3k8;oCzJH#2aYZuh06^qY6SvcF zWY+kZdp`=H9FuGYFmr>FOX(z1VUgu>QGQ&<=%IZ2AFrI=Ju*Fe0oCH-<_%f6+RC#+YvYXGQdKXTg#1v%F zhU8|7HLR^+t$#q2ic7RuE6#3hV{UClL+>lPZIVlnMhE9mUjL#U-R_ znL4h?*uARhPMMiyk~cM_(XtppEVipOSO}@I0Lail3Z;mfJF2e^b*^iTyjokBHw{In z4CIo7$rg;#@g3CnFj;88yOavQ?wRe7thRRofN2WLcpe+XU=AR7evFmu^#{ zN`9#rxVmfuRL{hi)DhRc-FGi6f+AtKJZJ(BIMmh+U zbAR8x_nvJRpA>T^Ko6xg!cjMnR#6Tb3HdFsfHL@=#-5)3>Lo~F{O|9iY zFS};B{G&?c2Oyk)#SG?^&ELmDMbE1W2Y-nF0fe_w20#rn_M4n5#ZulW6*c&EPgjQS zy@1v8PJqdPaJVN)<9Uh?H+{Zb{zsJv1gCmM`}dh*OxmGOw&bo!V?2}LEz`q>0K1bc z?vb?9Dt9LEGss|9lzQ`Jdc<-XgX|I=i{J?&wqNm!n!GP2O?_!`cjD@M#F*zxQ1;;7d3=tlyTD?$qOtszG z*PNnc*G;=Gy70goLf`kVIbfIsG{x0$rL#C(GYrK7c+$j_V$&e`JTNGrL6{iz22`U| zn-5)h;lZ0@rdbDl8_aZu zIc%`ocw)KzTv5i$T3+IjWLb(6BfU<$DN3lfEZx7%OQiilFTCzAUiZ59)-dJB`PqBd zu!H|7`#JvS?B|Ek?c=wi+iy`E_9JxrKd9*w;{+S#DoD=6dxqs!{k=&`= z&AGSceufkjq5;~AE<>+EuSdUv-i+Rf?nUpHJU8tV*?B?|){Y-`hkcoo(n%RSpZF1@ zewuXxurKPu?m!-cbDF7(A?Oi5jJh)YHASaT zphW7H&1BFF*tQpOR!-pQ(%~w&mxcuU8E>c}xjQ2c(zr!U>5Yw~e>UJ@cOtHG95i7R zw4*_Pf$#t>^urMpu>)Gv!XkC#NQbzrXhye2{iff8OET)!4Mi7@Nq<@!Mon2IXe*fm zH1eDMUJ!!z=))eU2i{&x81=2$zzc8`XghUV*M*|Lp`5pcVon3dQA-tKSO)xEZJ019 zc%ESxBMP=GlLrhy8Ay&y5;IK=D`0S$peiO!rh@4JjG}Rj%E0UxVkSc$<&+bKbyqYs zP)nX6RMnDBVXk7(RDUpj77LUWiieIa)fCNZ=goi^z`AXRF@>qXlw}kRQ?1IFqf(11 z+~k~6wy?f`l@J)n3VqJuUPyvu!SX3f-f34#s%RIcTt(RV?Is6%0k<%*W*L?Z*1*&i znOS5}6M;&QMOK*1N8kpGcdRlfe^EAztvA8izCUXV70zQ4fqz}E5fwAX;0B<8ML6Lj zK*7N%KUORheODEt?t3LcR#el>158`C%uc?_fE~sQVofy+&oI;)=vqQZUSpI{(2+cE z0;mvzQqttAhCydDVM3w1oGG9pOamh*l?HZ)Vk1vvMi0W{5@C#)mQE|nR$#>WeV#$# z91YCadTlssP=6v~BMuD4e5GXZqQW$@RfnTnM*G^7V28>_GERt^5A>!}iUtXlRzZa- znOv&5Xi@9E6xv^BvPV2>=RE1t2Cxlc^l=Gf+9_U<8jk zI4i7bDNH~^8z%ga7{-?ARDxsr#W3``3)F{r^j7tS2hDOCh2q~52L%Vv*in6eD;rZRfrSIA~H?8Q4h3zv3u2uB4 z+4B_pD#Qo%b&E%Zsi^_3VgqA?Ov^8w%n%(Dja&{^8{`x$d9gYwP7|Iu3NQP@aFpft zP7Q3>+Mey}meGu+*}tK7(%ZLIPlUN|?Zo;V41erDKtzy6Q?b^Ii@M{LWD|>t&uyGr z2ZVMccQSW-?l*Jy$!I9h^~_xPinNY)Z#yB%}khnSwf*00N4B5vAa6xhvBtTVd+a9<0H$qRg~3Ir{u;9TCL@ zD1Rmj#hT|^SAy_f1geO>5_kc-h%kDk>w2|fLD-Z_L9fr+re)ifc|5Gw=Nk>+6Y2N0 zLf0)_e*~_0ReC8yoSuTaUA*u7>#jS0-}sYAbA4AsK{a?RsD@!Rz%QvzQUbL_4DOiC zdM$sy%IGw2QO0PCPgADeAC~G((-As>(|>9;M=h2yF8_&q;Cy}Zsfr%gG~N^HyWkQS zq3t#l02s%#;;%RB_2#<&dd<~!SF?k_hBrKXGbZx}LV@vK2ffo-2D<>ZY?N9eUwT>6 zD3d=C?nz#2y1$o5Cn=c0Kqlc z4>*O+ZJPrMMte*k$L}0(6b(&gKU=D0eLZrl5QN=^QDshVZNqRAUeyQ)X^Dn)sii1} z4cH5`vQBCR(=fv$-Ak{$5fmM*bbmS($~V6S-Lbr_vax$KnBMdmTnk)eI(H~{lVl=k zM0HZf%tJhCyv+KikyQW`n_=i6+bf%Q^O9z#J4}-+ZQcUqAPj9Y@3BJywRH}7=gH3$DF8wyMN8HtpcDV z*mS_65xjdEtjUp=@`u1scMgN?@N7vr>FgFz_68Hoa7HDBK>CRaoJI9&LABIN3e8%3 zuAA&i3cX4*nlg3J&!E7NOLc}vmeO(aMrft7Yk9Wk6rORVET!5&1N^ z;Ky+LO>yKYv-CI9#M?rs)LfyeN=uxlb~%5fG{b_!SSbgE|(*06$Hp zW1$&N90S^LTwG?BJwW%9TYz=-)7T;Cj&v@Okog|6=h||XRc8=-a#oGMiCjAUG;>6bqe0jKE8NXUP?~#~ z`thw0c+^!3wi?JrEO~jQ}fq84s90Jn?fslcIYxh1Groh z1=i<@e*lx^EH6wwPo9#J)qFduApA6_&E`9%hzgU@de3Y?IY3BLNkY`7JFM zyz|or;sVF*CXkD*)`so9^uP5e$86-}5bvw|dCl_Bhv-k-9 zYU%pYMR%T0!V{tN)xsAcQ}<4#@9{3;R;&lf8?Pq$z4=hv*MhpF?l_`J?I9J&&$F_uT4Jk8c0r54Uf|ZSZ4Y z(=ha7EthKzA}P+oOsD>sfi2;ASDZX}{-M5o zg|_YSPhEzbLgG2&e?gjO*paDK&|Tx|p?}>M9@)8RKj#Tay=l2t8U+*5Z5-iy$4@=@ zAX<6w9}ge?;Nipn09w}?|Ky$W-{u&99{=wkdFIc>YdeGJ#mDct=W+DH=il?5&tvb= zBWKPWd6YbQ^vs!~k4|JH#9xC}oz88|9hEkFAPp@~9a(U|?EjjB%9ln0>s$F1)3Kg#G0pHph zt}XQ6$lRccfkg~kx;a*A3a^RQT#XY?d({mpHMmC(uHJTqg+FS7snym=fTG#gX*RDE zoS4|2rn@%P?x;=gZ5%pyzg-Me6@Ls}dt{$dXH=!Ew30cV7l$ossGeg6*Q%TGDhodk zJ-wJaFLxk!xg4o(Lne933iaEwsA*%Vs-dbGl8_OkNKUgz#|a;02UChD_lBL!116JV z`b`O8fQw3c(rFX(r7z>|k7}Tqzzi|E0hZsRxXCcr0os;1fGxTRCI#Q zW^j;foHRY#R1MPuki+px3~F$BA_o=x6X0n^&I38mp-dO~VN0fxHQbKq2xyg_+raOS z8jZz9WBkYH+v64VsT+6y=6}toJKnV&-GRPRhXafC-yfercLw86+;QZ-d(k7~M^SlG zA1#qBAZy*+{@lf>Es~mOHX|z)^s;0Qsgv3R)SRTzrm6GNfj(-=NeG<*YzMuRn$N=E zA9OZLOXk^p$j)z>E9bLS2q5CTr?eye4!SPf3c5L6hcYnwF{=K&*Q+WUO zHDD?!YdICyRLv_tesxr>&sFQ#*}r#rr=_YZ`(Io0y2rO4sryVjy7LO?mZnh0f742C zyZ5%Fh4*gXA-_)n%tB9Nf?`9v*Nfwq>a%tDds(a;^hcaq%I%ONER8Ho($&*C4>KEz z(yU=Q)g+R%0_d z0sAc^hKTpvx-VIsLd|V^<>xBCJ-=(o9L{cDkzRZ3!1yuLYTo)=zrGjEd1`Zsu58_R z>u&V+$yM9-?nov&X@P77{`nT!XHe4NWD>o6CCwlUh6GuVWPfr1c4zTjY5ciJQVb~Z z;bxFG#rQHFpC<9hFf1U%;XqDNO=nB5rOO~lur5jIBq}R+k$IJYJOeS|RA`(D%>#G{ zcw42KDTxfOJJS)qnDWpovQQRiO)ycc!Hm7TvbE>S>PcwRp#G7InrwFo61uZU7Ed8;$0A zDO3djYZ0H0je@W&Ko%vLi-w7APt}&aSwQXZQ0f@w@q->yG((aItwPOwbqd=G#adi3 zHZ*1f0TG1yP_MJRszDk#KgAKL}0Gmiys6@Stk7oSeS! z{K1^Z^M85}mMV@h-Cb)^M~eQ{kbx zC2g=Y-zRFPDH)Prr^)=%Wzx<@dX}#-$i^)E`SK1UnQbxxyC};7wz0#PmZjnw zruB`Qa+QnGpd)yDAX&5Il{qjK3}e+G z?Crb|2*+tFo^J@m92=41%z0W^oSQQu6~i!fx8h5MGW2Vr7tc#-0$ywWyxG+VP z6WDLO}T$Vfnz+nI&YH39gnd zpIJ#(;O3&FSTP}1Np)OIRh4zhD!`|_O`+{(p}d9&>TaVyqtOzazK24+34fFxC@3|o zy38CVNMQLCT(XSlE9DSqqzt(s0G5$wIT#xh#0|!3r2D8~5S26Hl?|0UBFM|M6r!k( zsVVc7m8n>Dp`MCkl)-2@0hLWfXmpJldoIE_l(T^)N_9+{&XA|l)9FSPuI8VY{%4X; zrdlBsO7`H}xem=@m& z*dgfJu1H;anePCe(CG#89fi_+AtTQN3CIVaR^h5N$pVbhpd;r;0>)aNq;s81xR%;+ z2z@n(dVb)`B#lI>nh_La9Ys*qWUz_?s7G5WH@KV}gu%Gw=o>CyD70%v5rmtW9EPb7 zo@Zjqwp9Vc520;z9)B0iP6(PrMTQ*2vb(axBy1M=H0zeCC|yN@DLRY-l{FdxseF(X z(0;ZdrkLU06~`CG^p%J@k*?Z0{s!F5n}34pVPX|^A`42HX3Cmy zBMm=8O*MWoG4d)>K@5e;-Ow%q$O0@y0e1<(4YV*bkLL{2RhdgoTQN0Dz#taPG>i0@ zdLr++PF~KVCl;tTyI>04auwSEojRCbnw7fDMrs~(itQT_VY*`amc?|!6@o7nswqH% zsa#`5jszjcrhhFMG+llCF@(>D3u05wA;)5HA5pkQstA33&-r-Q1Gd4CWzlm!X*?z( zH1DF0m2--xx@!BNr}7XPstC zNev|Uk=bDBe75v9z31vOmWNk*u^Dwkx4{5mnf&VZ0>Vk7hj-8@SC zz=*pmAP1m=fr;39peCN@ilv3sIOc|}a5Jy9VTa=QEj@1s^<=Y^Eb>J)QN8BL zLEJTgKDT^F5w6$P@+Nnd3S@O*DLo$O_Bn2X@V@}-izTx3{50#UzaS@fvX}qpy!^+r zjg^g+Mt}N&+UK_Kd)e+cG$vb($=}9Gy}t6phWt$Tp3Tq0Um;ZH!=k|J4SG?J3`WvZ zisI9Y@wE?r?vaO&#RNStzV*QeQRuw*n|GfrH<*$%?+Ae3NW~2myXbo;kDQmjjq>B? zskU4f?=2 z2Wgny%A#VO&D?u=8USTED?9t^O9_}@I>0?CJeD5u?63qyYc$IM0-D>F&?hCkUO$^p z2PXqZ0+EwKm1WH4u`!p8O#9noT=qe8}Fn0hl|AZFRRA1 zY=3IT&iRU8$cKTVRP$00 zF@aeEm8Wsb2eJh%uWA}mLrtdLaBOiEGzi6NTrXZ{0<@8S+=y6AB^2ub95f`uM_^AG zqjb7j*d|jDR(CV_3i&~>d*$kXLTNVfhJWSwFpmQKP|fK4@skGV70Uwhrlt$sQByDk_0lv>RE@#4+JE5# zJxiulboU+U5{22@?MH!fE`62z3^?aQWe1Gz@rf^Z3i$t_+?}}(Fb+eZ8-}Tj9?nF^?RdC)ns#xqE5KUHe`~b6y4Dp zPL=t_MM9xai)D;6?el;_gLl~z(bjOc~Vq_5^4U$l0F=N@)Mi_AJ3YqoFHr1 zk(});G;MaNIOW*{=()p9fK%lfrXInf(wZ)o!9c!C0!&qn^CiGmAieNYH=KEyx@Cg^ z5H;_rBo=vQf*k~93uNEhmtU;OHIU>3RVaiIRG4KhbD!iYb%X)D|A3MxukHl|d9q{- zP(DCcUO!(UV8|3kqBPW+cWBXo8?*W4stJ2F)r`SdGizYE``qwoNDM>ht^uYz54Der zyiQ4JaUp;k%E>>(FsqbmP>2;k=n4~YdD?HG%cPUxJ{Es?LS)K02`sr{k_y9-k6*bV z`3{jrorEepycTNV!?k#^j))-xFEBzy%-5U^yL63At~q(uU-c{kDFuzS2VDg0`D*kg z^fvT6=%eUk=n?cE(dW@0qd!H@pua+Yi~bvW7X1_Y31G{YQvL$00bAGuL>u7(j&T*& z@Dy(0HlBaQOL%~{;dQ(V@5hJmCHMrs3}234fnSMVgI|lU#W&)c@Eh@)@fmzO{te({ zZ^3utci`W`@4@%u2k?XV1Nb5QAMnTVC-9^A)A%#^5AoypkMR@u3-~|dC-Kwx8T?=H zm!&o7rW5vi!@)K*fbH%M)R#Fx71Z07#=d}^_T+y8fFn3EtVtehBM{vrbt@*ru8cp+ z1^)c(Dxc5dHtgu7LA$0Oz)|?yf+ibH9X4?f45JD^2Tye!!|q= z!0&(NQ(6Fz-PCo>gF!pHX$1QQ={`6m8Dw z-;R2nCX}mdBY8jG6j6c(LFkLDti4cl;8=fP%N3ABf7kI25d-B>}(SOCCM@1!ut>;IhtSM~8o> z-`HOs1f%8jgtKR3^a4&@8!0nTJ%Ll-Jwi=T7zv?@5Gqac5vqi!aAXc6(^JJkP+R;a z7?ACcQugG;Ju-P={2T!WF@8?!3D9B)aOn6!P7+Gc?@^4E@coOx?H&|)3 z57Y|XF2Lv;a!FSmMh84Nb}(uJPnLgb6N>SE4EOk=w9ez>GEeqXQcaG#GUrmPGU-hK zmGoSS+dY+1#LA$dE|(XXlGy(E$3R=@_(z1)E5|q@-3`1#v2EB1A%6{_Yw3cuAaF?J`JG_G6%53JTZU%Jt)?r zm{zASx**LD#^@f|Dj}2(xm~nXCc?rErKwTUC2OP>Jx0*HT(WBX^zoxd;HrxcNy2o2 zynH`}o*{eT=Xq=L_ix=Hmlw+B{2=st`61_P5EA62tw}p;auB8G4Pj#m-b32WE|Yrv z>|HYoRjrJD}PQ5z|jB$Zq|plm)rk5`7Y`fK$~k zaKgVRyZHhBeraz-D4T=Ek}?mVg5!sTe9rjxItZKs@c%B52NaSA0dh(S!Yyc---et0 z-G2?gO7XL)m!r(fM5g6DiRpUkp?LwG`2O9P9>EA$JenEL9oi>zUdJ^;6nCAF8A1%t z{t;Xq$mCx~UX)%bU4%rgo&sho6M!Wy`{LJO>r!Yt{Qdg)o5y9x96Nd>6#{f{U)ove zC*UP$wza3^_8(2n)y zPZ9>4PTooSa>bbi2@ETU^u`DGW4641y1!>X;`7x~3m3+Jhmw7U*4a_v>cxZo2<>O0 zQYUz}yzgc4Dmzz@vG!Ioi@~MA9N$7-HZ!8%?#Yz@EPWF_z5mSq18X}k+;>|twSWIi zr?IrKYiaRJV-YVTjfLgM_n-Og+mkj%`**D0cV<8O%cYIwmf!43S^TrEqyz}Z$6EhwgXn5Og<3C3)#7|DRyG1cXZwLHK2n5v$eKRJ(|$%VOMu9}-oBOWfBi!Ju# zG7;&zm8)owPL59}$Ty^qY=Q$kX}V?pleO*|G2#}{)AsTbeKO<_Rh_(y8xfa9hPfecCtw@ z;W>X>x(02!IEHM}wQLo$C}{W3Wv!>h)8S}oJC*BDYoQ7T>LrtgMmIon{Ul(VOyxSc z)!e?^>m{p`v5kt1Ck;}Fm$=z_jcHz-+m{@!5p@D@vfi6qFDqLLH<_$`FIPW7A{|t- zawUnA{YDsnaT1#38=fL!DiA8(eDf@j>fUa~TKi63wvDmfy;${H>+SaXf%R5v-EOI- zp|#|EnrTJXm1)=jod7pL$iFlX>hwOsPg2eo!~9Jz{Tk_uw9P5rtIlqFJJj zfnp#{DFng)W8?Qe0~<(JJ}19SV!|~Sq}Tk%VrzV#uFNYsK6ax9-v&B*)8zLST`Y+7 zf3kVin8@$7`0kv5TFBWd9T>yu+D(lCOXNaMgI<(=hN6-EPJgi^=$-k-!|0ZmzB4^@ z^x>}@JNht~s`m8E-1M;*$Df*+!FRtnH}le8-g@i956^wMd*iL}@8P*4=f=$DGs9dn z^*R3HXR=@Z$gdtd`cZDcR#$mWzSC_)huvDX0(Z7pLT_|7)EjLs`;tv9PD7?uxx{Fi8Y36>FO>6- z)71+kCk_3HyE>b0ntErt($N@gD%xImuz&`wkzCq1o3@%drFoRD_?f0|lgUXSEX>(l za7B89$ULPbHJxHN4va+pClQXK08CdDmj2HVzMgtkxI%PL2Bw@Qr;`;*CIRe|JW3}9 zd=kdd2jG)wN*pf|`ef}k^V}+fK>jsBLi&IIG$ z``*3pzI}=JUPNR>#+LCS_a)Y<%BoDMN+p$4o0b9Ex%Zs^*xC>(Eo|N>34+)?^$2>MSjIm*Wn>0>k7#A_`M0!<2yf?K^wG(!&9${AxxcwaS10djZh}^~ zarTMFnfibGtH-B5%v@_uZdX^{*Ib)~TNiF^9p3!PaCi#-dcE%qlVKTNSjBv;^gebO zXg-6&p~A(5YYI0P3gfmCm$+CC#NLlxhI9<14pgc9&WAf}I9R9MVP7pac4jSLvkx6$ zBQb@8UrftPew zPIj!yg;(El;LyufmyedonbFIZR!=OL<)bfr*`WisTzz51>YQBZzQpQoXW7AQ^7X~- zth;~RwGLi#@qsr+-qE#}x$z%gQ8kUNwA$a}p+(?3*F)dDy6`r{36657L|h{tPMznv z)8N5g?m3}isN!6p?g4w{I+sh(!Afm=WNp=m?W*k zt#ZVL$#jhi=Cg(7d;|Ug$!DR{LsK)Umuf*!Hn|<(G}aUCxEa27Zh>=VF-@>QuTH)| z$Xo?|JWNA#;`_m9lE7COnsqR9N!76hlwwC3J)T)B7F(&cmWnZxB9y*Uu7#~OTke0O zPXTC1Hcu=aw=&Q`v&@7!3eLl0+Tv2JWC2qO?eEz=C$dThVV)D5r-+BEU+2U;;tbSD zdyZUIEk_99VCc3lHG8HD6+v%&GmKrK)6QWBHpp-m41t?KU6rRrg=ytJNx-~k*%4+E zV;*<1+sbJOw=6+W;s(*oy-pqHF+6_}Irtz`bfJi1Fg}k}ipRj9`S1o*JU~xKD-ES9 z`?b{1cx1Lr(7?4yrRZ7IHVO5M3&lpWyOwY-5q`BE*~0Z~S1r`ggwkl(1qP{^6gPx? zssoF*HZ11wRF4kL0h86B*J`}+26#W}xKwii#G=oqHB_rCUal)Au#L+LrREIg1H zh)H5XS#HhWyvQhexOt;%tJfOU&C|-{N(dIB6Eey6!QA886Y#nm|R?`Rq9+W zTNUh9SNQ=?(MfZ>JGEPr_7Z@R1l zlrDZn?;_|WXHF>$eX6A*6Zv5XSBm zlscZ(N~b!_GJUG>p2CBLKg!Ww)rL_zgQ}ad1+}Au%w`5#JvH3YWQKHsHxBdc9vIY< zO!eA1qJU+5N2Vl zq9}lwkI1|9N)mBt0!x1|InW?Z$rQoq0_X;VqPq?wPW^zUcY~5$-d!I$aA>XW`a*@k z!$^Q6KeW(B4MF3hm7S$yWrS)&XE2TVRTS4C(}e|$i9#8|G=kPDn1&$s#e74fy6gQW zr@(@AhWn^Y)1?+VhAFA!CV5LkI-F^y6x@_PgMly>Qc8r52#J5pv|z~X^4dkmE~?pF z1ctS|FxMDJ9dYI`2A)RJGeN^z-@MqcbwmiUbiKCrBGc1}&NX6!WkW}EphDS!amuoF z5g9HoH$0oiz`KkntD~W-<5B-M7|f7i8UkR6&~k(Qs%}WAik!-sfJG{r>4L(gg_A1& zRQY@=WO)H%K-qt+iYK8%Ge$+K)JVfNt{NduJ&aqYH&0xu_NnBYN z@wH=z#9FULU(Q+q0`sl+CPresaxL0pPV6u{zd8wrrt8?DmLAj4_*B558`IcG3S3;wsV~~S1syR>P2&t)wwP_ zV(z-@_VoEX*{LZ`VIgY9O^nMy9M!w%{JFpXTa3OTNB-t-*vY><48MhG9v`2L3w6+@ z_7yHH+)(%zg; z|7)>Ugue#b+d_Zls2-SplGM|q$zuQ1fkT7MmPXF&;E>@x#_0KEpUIZx4TQq+hpk=+1-BP;Li5;HW_X|x{XB{ zQ$HERd0iu05DxD};h^i+#2evyWs^&Lr(AU$OT4T#Te6rU& zdDwO?IlUnd>cz;nDw9vuSL*QhME(zfLp(P$9mfn^&xuOqh1N>v!216F(cS$!qeEMV zj$E}gx9ys4vorb8;zIL+FdU77=z@QyA1$rTMfUc#9nF=vVVXv=u-NEy?d_A>&f-GW z4Gv#E`fsM&>NH=M)a$GB^GW`1ihKHp^ou}g^}>O|)rA)oK2-Qf;gf~W6#k^}#lqt- z2K$Ks@uyrk(pDZVF-zXj9%bV^JO;N@r&(?Cz?dD9bq0OZ^px9Aj@gx-Uw41ix73oV z%9(&(n<7z(BI2P->U(gU$UA@q?a*ExQ;l3`worj@_=HZ@OZSYy&Hd#3m%$+t7l)hi za74dy+%keFkY;^BdH0&N_SV9@sj2AA_TTHW+A0S%ru*wAu|UEYMEGvWr?p8HF0TdU zR@Jo~k9sA~=fp5HYEijP?!JE*1siKMgA*sHfewoPx2Z_Dx*|j;+xxmw11flUR=#Wa)@fScL6FL|mwyvGB(-oM~VKkt}pXom{`60Z03O{_K^=2X7+`5fk0dKd#7xJFXUx(if_*ldf zcTbNO3w!!DVMbxCFa`_uUtnpHVJ;Z;TM1fw-FuOqDrOLNp zpln?95C*-p1`}cGudx?*o{cdmWi-X~N_!*n{oQ6)R#zjBd;ZGjKw~1W5PG%l9~@Ld z3)H8DX7U!d=zFH+-m#Nk3dU=GDZTZ7b}7}m&3$H8FYhdE8q7zh@Wr-tnCWoGl9qFO zbAdfgv)v8s{&C?sC*R<~jNk;Nu&G{e=_2O>Tmodqd%F z(A0mw@NWvA&m+5kE(t}AG;@8`W~(pgYXk-FAY<5mYRxe#^$9@?<`L3?%pWz9Qr0a4$dR6M;E_aI|8jHrXNT;UQfb*FB&tv8p#xV5M z)?iem9_oL5Kj^-=hUU;ino!0;Jwlg16^RaJ?fd7_k~HUAYfhy&S|9H2tNQJrmX>?H zPWc5ZzEOmm$mA)biK~k}qTg?GUgc1F`STbS!hj7>7Lu!=HW8u@i1-;PSqRp2u@Pl3HTPm^&Kg zD0$PUo9X7S@{d;1Vv6J0wY{~q*?gGLPch%rS)K(;?S|^f@7-yX^kn&g^ z&FH5&D5-PBg%oj+5g}Hp<#L#U0oSWG>ggQlP_){&q9lpz za@hEs4l0WyffSk7(5zv#?AXGUj7irv!JdEP(R(9V-B(W%zwD8>fHDY!ZQJFhTPX)w zJbAEJrIdz^vXjJ>hOY2z_A^1Hmbm4TR}p?)o|-&CFu;au^-?jmQzL9}w(xeRoECc$ zBTh(hIYRe;Axu#Q!gr1z<7F=V*JwsyU_eP{pEmh#@Fu_E+EE`U9q3IY7&?lizud3O! zwq-KtkcwkjZn@~=?qjK!*vEo2==y;r-dF80)~j>Jlfkm@g|edyO3GPm28N8PQQ2$g zF!Sm`HTHpf`m)56Oqzekt*Vn2(@=kKR~AJ%<41!q5Z+c&@xi)gLYEx0KQc6`hA25d zE5Ls%S`%(H7>a|3&EiWBOQ+U%Iu58mGaLlA2J#J2*%r{U3!0c|0t0c!xvnkXwL-gU zw+S6<#Uu2NbN4>A9>sC=TYviISH0@aFE%f3Z|uBcbFjkJ&fWX$rBOTT_11s3$Xf5n zksqxtj*~r`A_e{4F0A4_bI+$Y?*v8Rs>Ex!s7e+3V=x%*FqlIuZSFggd&ajWpCUxY zw&BV7InzsG)N*7{46G)7S7TqRvF{?h@S4em)3fLjqOLb<zK#qc>6IJ}bRWtlwkGDfhh8c!JMI8v$q<{^Xt`yqbZvf=xWGT$#vvIB=6yt&8w7)|!J|09kXg>ls3}euV`tuBRfw+j$I*%qrXpbVCt&+W9SMkv_~s_j9$+sYK?7~Ho9ZZ?F&*#IuU+~xfdx|ZWo;SL@5 zal;Y{9c^Q@1tO;6zGojSrXgs87>70*Do1uqKAiT|zUEGj*9`jNF5Mw@+VABXvZ~#< zt%+Nz*mX${-QRyjXgxrcT6|+88r_V*=AWcM9H*t}^{W2g(s@SEh{$ah*VwhADcQ_OKBdc5HGU zCuRmx+nKHXMAPM>UOK}=pdV#Sj}*uO<5d&I2`;loAVGf&gxD#A#b#q3Od(CJ%xt(& z4>64)#id1zVbfGnUyAPN8SYxboQgCIMU~vKaRUc;au6|!p-U=e_tk7>QC}uZGK|aD zF(gx9kShEonsL5Dbx8$!-Vv-1Mov_P%{+ zn?B58V%x%@X4#HGN``1vY)^I|yw8)(9U>V5JUM`6@LF^j%OcY^EE^ zn-ufKTY?%GK8isOEW!0~4uTgqjdfBrH5do@3Z^r3Mj3>)BQe8{R!$h7N0LCjQ%2Z( zY9xQo*X9?-L4#pXG{(Tr22SO3C`%0vx`Ns&btF|)&@eJ1lAsyDP1Wa91XKcBmmu;i z)`y_)Co4mxaC8nHZ5UCKClFsE&@~+VH&RPdUJ-0{iw;%i6p4d4(`km zYRg)?N7+%_Q}>{&)o^+=e+cb|opI<{!v!$oc~mtcTPA@)asFt_^JN(e+gmzza=L;3P zIL=P3ATl3Oak`DImHNV_VJytAoq54VSvsJ!mhEj#qqeiL2xRz<<@rFTzSn<_E$D(T zTWM=)bzw!M;GHbzh7nB67<6`IbG0*oPB@S^vcXUw%*<- zb>}q?^U}NXyjS+O{iXf$jL+RVcWft+_T*C_6{2HNvsYSb0&6f}>Zc3MqwW=pR>+*Y z7;|;QV!#vlUCh>Wp$ik3iumYV+`@eIFji3dT^MF%h8#r7JMO~82VH1}f~UC)Hxx~2 z!KHh96^Mh>E9_$S1@wR0#`!-_(;Vowji~V|jM$^IN#8}A0d%SH^TTi-k?zqOM~zCU ze-#*7W~`#k-v_0YReXlHk6m;&Dv!=?@-u6vuRp!94sYwUQLdB*FX(}-dgO-jQncAc z_xblByo1BbAjZYLYdUu?xoMpfdmV4O4&F}9@(t4OgKd)(8ft&_?!v;U!daE67oorq z2o;g!go>OVf>PLN&YY6P^Dod9jSZV{K|9XZ@G|}9E;xU9?^)!Pm5kD?k8hu_&BaC2 zrq#(;Nzb(Hca--|O)jxb%+ZEt@fv&b4RzlDxOImZUxgF)xu`H7@}2_!Hkf9by6G7P z-ra^0LDhO(my}CK`VKcd-!wfJ?z0SGYQV~5*)~CuDG80}j-6;&v{&^F4|F`;fb*h^ zhswgj9RYvt%=xx3ea{rW6Q>fiL&M-^WD?7_4b*l5)3HIvhM~S)T+X{0jodHjy4-~FhA((P|G=7>{^Br;@ zXgNz|T`+UL7*B3j-`Evqu^EBl^#u9_w!)43E;)bkpLxU&R#wQWx??d2ncH=Z$?ZfV zt;Eyd8za})<&`2>lAV>}zU45oif4<>3-);GpRnVq&PUX0dNxZwqR@Tj_g2VgI}^$+ z9YMy@kwO=1t8B*V(6)-xgG1!m!-2uvCa&3NE3Lt;{f#9FDsZuBfYRlaSC5;LTK>J5 zv><;9mrhARP4C?iypgwX|B{2%rT)H!v6qr^{Niz=nRX8MJd?5S7?$IrE-uXo*ybHv zO5a3fOww83M{Evc!zqb(uO)w)#@Clx@oEShltqjE@=|l>h;5fKE3ei$ zoi>Wy;>C-l8gZ@pl2JEadf}y$PfN!S9gF_pitQ5@{mNxc7(DIsuf68ND`WR!pK3x+ zwNGDvs_2`(fC12}8bIvGb~oRY_n zAOFZ{N={EMhqqs{5x?Dj{yVwgg_~uX-bUi=+%qrSDwFiK$q%yMppUPOdSq^H_5Lk7 zS6Tar0hinMt7ZN#|3+@Q{6*J0lb3&!_pS9$pS{6_Pw!pZD4dsn?*N_TrWux5{u|Gh zc!yl2%9$;)^i;e%Fu6gd1kEm0$LGoO@@*s04`B4CsI=$Ri0&W*1JDUWGa7d{=N;n7 z(3PGBHmFR>ezsL!wOxuDmMBgh@t1*BfpCXkFls@H$JOOQ=!Ag{77KGt2L^w}5v~|5 z9%`T;F}2y-IPjBX`}CPxdJW5=)HVX$1g%*U^{qn})vwy>Y8GZ*u7Mr2;L4RrPj97&GpK(}BG@BX5oNFd? z%rsdE1M8^fO4Fpm+wLznEAHmOY^T+1cUzZ^M{^}3mg&0>9+#9hG`)Yr$Q)RkVPKdx z;h=x{t%bO#%N7%0brR6Uz~-~0AA;_s;W~o~jMVJSq&!>u{7=j))G+^sN)xBtB@?Ah zx=MDA#+h=OO>=NIm(HS1M$_OVMO!F8XPXR(%0Q0mNi^%O&!{h(=Djh`kw+nVEo9f${xfJ;K)n+5h{J8`^ zwlS7eN>CPG2%m&!fcfp1xP_8=m6VhJKNkxgWre+YU12qq6B2(o69VI5OcQ$ljhjJ} z-13RlcK6K5^-6ka{V=&>axZ;b{nE6ucKpm@d;O-#z31(f{uGc-HG<$(nBBD?O}Jts@bc3rD#oMH}0A<8_{eC=pPHB7^1 zg1rZt)sZdX>xlxG$RuMraMmz%BQP<=;Y$X~OlI<$9nefd2UM=K4tC-4feSWu-K`dl zssTm%jF+uQr}yCsN2FU^8jejR(IXu!m$EcllC2~SdkKFrq=dnF)vX(P0`;;Pm_tPG z9#~%Hkr|tQ2s4J;y<^_IH(F;^Ta<0KvCm9A6XOzAG%(){#tmAP+}C}rcMU^78WZCo zsI6Ck2&{6*tK2MWzRs%-*lT{|K=NW2}4t&7^RiaS7um43G^`Z-hECQ*K{M&LAR zGzy_fE%$b(X@$pajAj_8>Is;>MNL<ov$Df9 z-IQhL$HUMOG|PbK!(JL|jCSPH_4>E$#qhEc#i?Evi%!Ud3)A#T6eUlzHZL@UK(i2Np&J}MvdbHJ{+AEJS z!Stn$Htemg68*+ArtF;0A?g<5LRPpe-?KLkJ$2w_aaL3fB0HWTr znG=7^OfltOxJI3g$g`jA>=SIG!yGpG{f+JQEFsrRhgsI-4{Vbi2d&#$u|VW4?KTm) zPDqXU^?E0=8}>0!ZJ4v(=Fa4SY`@M9tdScR_le2(J{vibTyLAEz3&t-C@lffsc$6c zXwEE5Zx^qv^h+U)ZWT;A&ubq>q#ehdx}Sd!s#7BdgP5WfOjXRsIveazwR|>IJHsx` z^Y>B(k%Vf&g#6Ge7QIOk-pS&h9UY(bI!m3JZ-!-H&W<50-z+5tcik{7cIN!rd}4ai z(m`Wb@R&2b)5E7sp4!~}tG{~65Jqu64N66r*uW8i3u+eAq2W{(=aae2F?Gukj?jNJ zS8d5ZsTkx#Y`(yuWficUw+e>~SAgw&1L#1nDco6jf8isA&lmpp!Ve1no#>=RM&t;2 zA$ckJRq|Hy+vH>9bL0u~6!{kU2P)_my^!9hFjDTL@1Y-{e?%XmU!wnsK1IJpzfFHa z&w;j3Vl~!cT{dC|*!Gu|H>j&A!FH&7RZd zwU)M|U80@TUZTBHdy{sj_M6(fv`=cE(f+OWC)%HDPiudxeOI^ib^VlnMn9|nEByod zC-u+hU(&y-e^Y-(|E~Tc&cKdo@CClaFX1Qoi}>~Ym-$=yJ^TUwApa!)6aIe{{(tZv z2nOnoD{5jzY=C_^5?6>9ieC}05^oj1Dc&XS6CW3kioXy~iN6*9kN8KUVswmU4J}kc?zajrleoy{TJ}aLyS&j?4)gB6T zL5kZgu{G)|TOtz}VwZdJ=GFx+ODddZoKBdHTSZfJhvTk_(ZH{$G_=PFFue?(n0g3W zkB)bu*iXiJL@VkIy)mMydkEqMh9f4;Q;G#@Bw2fx_OLD~dRw%*csYOGljdsN5I1lK z>R5_sVY+6@F+UM3IqGe1X{}J^m*MDVk-)~5$gubCWE6G{+z}b zLeU<M7CL?w) zuvdjXeF*=sv(-B)l$%>)+$P2+!qd|609EHW5s2#r+!bRdTMB<$X9~EBPTWW*MbSo} zEWBql?)GBQ?vGL5gjW|Gtww12Wa%L5iz1>qq!}~^cLqmLUXIv>utsqI<{>!R=GiD6 zrCWFkEZ{MM`Qo!;%)c=njG_AQN^sPGfN`0Tr!nS;glcV#`$d7otumRyO=%mGc8r0q zjuGU!yA`*gCPROyNE)H2;e(@PB9X#hs5Wi zn^G(9Y+TIDt$DbA+#i9$1=Z`PnLvS*A#`V!B5hU)J_}Uzc-)6JcDIURIB54{7*MF# z2qzep+ua1|HpbusOu?jFk#p!|3}eOYm7@&g1+-jrF^PXBPLm>ZRXY=1=;;AiO1Wa+ z&(%Gg)G=bO2NFsHFK}d_Zu|CS1KiPzU4RuptbSm^V;lnodk&Q!Z$Y6$cr=V!4_Fb$ z$)Zk9`87}~5X2B&rJ$j}3EBdb=aFd57TSN>Geq!1WNSR?7h7WxJ3v7(badJ(qNqZq zfJ_PMK0pZ;=c^Y2BXZ+zJHan7uraWWaXe+eSr^(gR3@47hll$!m5UAA2u!p)K!~gn zqKd1w6|ttk$Jzp!1&lJt-2wWYW*GmPZl?WK9lbK(Tu&f!iXvjsQvABz#Q;lS3}b(u zjBw<~N^Nd;fi!Uj33y%+2`6nMtXvP}EAR>^qdiJEbjtuK6(o+5EqS*9^MC`LGc7(rD>5tpTXl!Rje^CJdbL*vU0_mOi#scL@;cl$tl zz$UgpYHcaYADh#{Nr7~Q40dx1-IsjNWQ%ki^KP$?^#P?018lZ>3f)%>>79fc zRwHyBhRcMGYiL~8HNMEW?-SqGsN{&N$cW)njV%+6BW}0pDHw3uVLI+6VAfb%+Pww+ z8eXXBmZs}BBHYw35sm87kdA-mqTd8D;5G}rKBytNf}D~ZQKvMFm;hr?!{s;PU>G3L zxopD~wvW(Xm?Dl+hQ1nV?-`!L)G>pa4xFe`g%uCw65K$c80P6x5WEWu5mM8 zLr}3Cv<$KEW11&k*3o(BCb*S?!l)rUyhYTe(`C5-Pq;D!WuEDaVRs}dP#aE}Ic8gz z&|(fZ?Y{zfa>5R>heKC9w2{=iFH&P#SEm2X+9zMz@?fW3d7#7yvEg^de)<4 ziL&2?k1xhxO&EEF<;8Rhnu~~WBSMk@PN)VJv1*!jEg@edQ7aj z;q^9#=oyMcR3|Xz!oR~<+Ly8JIf7sgZ~j@;OBAV>k-VSLg9nERAt^W(bUz{Q%A1W; zq@JlLADVR(J{+RTIYd|0XF7X7rL)0P=?BzKCH5%P;`0O)P=%gB6pn}br%%TdhZkBz z{v+J^^=WC8{3U;O2M(+-;Q%6(kPSkAkC6AP^H`?pF{lkE)5>Aa0~Pp>2}u#ocv>Yx z^$9w3nn(e;7l`TzbeYo;_8n)b)*}r}0{Kfw!$LS&v3YQ`EVe3qR4A$ET_R{E>t`@pCabjhNkY#cYF z(a&QGg#wSG52?n%Q4PBCZp_4nZPpAeZv)DV}!HlIF|DUQM#FucDsojyci zL8w74qi-Rt6pEjLD2z@%s~Iu@`EcGJ=uwE+2vmfM_&-h6z$?!)DE;J&pC}5Z z&?hIMs4rmh2bOb99Y;VF#!U2J{+)cBkq{Qu{e*vns?wNE;ERlFCUX%%lJTdJ*c9^< zWJNi;5%Q5etUz`%VFr@JyRorUyd9Isqi`{NiKE1U^$MN+1=bh(DSMv08bYLU8e#My3e+mbHxtk* zDVK~(q1da(a^FRbc#H@Gg)FAv5H3i5ZLUNykpnGak_XQLMo-Mdw}BX-Ph=KBg|vDD z6EOHmjE~EZ1z}nic#4QqBm!u>tOYXkpi(fS`%6HgiW0bOM@8hlR%}aO%|%U86P|ye zDNZ10P!kQD!A{X~nT<2SWJaKQp@b<5m((jITQhQU7mnv@5vJ0?=_;tOsm(k`Xviit z4pfAM%%tiY3pqV>uMPb#iv+0AMTb}&Cods6??nC}k>nX!1Y*~!ZWY?IxB&sb6^KLbR<=&)eH->mS~nm6c#zo6Jc>~ zamqXwx{4UM|Aliz>df~rLWB^XBbW@8a5M;%ZiYd6ARn1& zK^U5lIyF_^C&E=ZO5?*n!-RX$BiRFi%rw{L$j)g6dL?o(s|e6I_hNq&DM@pHFm21g z?sH6q&IL^>s%9B<0ZPy%`t?~jsU+qtN4-_EVdz=NJdTL)*bP9X23Hy)YN9$m2<8<` zN`q^{0g^0Pz&!|aQBTGc%{*uEIa8A`NOoidPyn^cFenb9gus{}G@1sLv5X=}HJIs< zjqX>N5{M~x1!6itx&(iYV9q9?M44h7Md&+VB)XDchzzESTq43?!j-^Bn93)mq?j5R zj&2IJt+-0ARpTfd6n*&_`ke>(jU(VRI(jMN0jIy=z@Q43=XL}|Xli7zNV=>+g#o=u zRni>PoKfLW-udWlVWAi>lynDb2N6?6+0j0DHIX#gLUXf@$12W5;JDutDaa@$Hg)Rr6| z7leUgHn4VG^DzI7J~tNt)#%E!5TJr+MCL}EhqD^m4Qd+k;bNRp$gBm*8q7V+U^s2W ztR^rw&cA9eYq$Y@I|QX6Z0MS{n*53+NNdbUolI z*rO;ZQNhLJXwa`Vw2C9TorZJNCWdWz=sHGFW>AZ&V0nKvSQ_@ct{|e&b`q#>>ZXm9 zSuzMTUqNhZCU6MNMGY45qwrR)lqLm+jYFoZxDO;kWgTK7bcBCJMH*!)Q5S+{tJy^O zs$wR=?$i;Q9Bth?X`qE_phiw}buCwvXXeA~YyL*HsfB?Gx@DH87v*W4JVV&fa7-nh zH_gS)0*!x&>B=TU?F|Glv<@T{&kabVJ)#x>)Wgwn5$Q{%T~aov5Zotk>T5wLl=KUVYYL4X+t}n$oZfJD%`J49wR&Z z_kYXsK>{#?Mb+BMMzz{*Zn{O&Da7jPZ$zoqDSYcRMdXs6p*sPVv z3OOhP4ya(#oY9uY+(J~CM-=dxL#gUX6`jF?{w~?Xe9!~(?1{H#T}n>u9zJ#A&Q5i`8{UmIj>377TtE! zB5lx>&;I_ItZI#zo;ad~SE!@V_nkPt)F0i|c>g{aF=%vW;d{cg*I0*tfB4Zph`!O?R56Iqi1S z$rHZfxj?$2Ju44$GW6k7zYA~Vqq{r1rw*^~ z?Cz`{K26LwojmDZ=b!#usa_)cpL>q{+SBkextsjjK#;g;IH3G&#cUULmq3DiNLI3M>58QG6War!iufF~aVIL)rP0oGv z(FYzVKk&eVAAH~ea{S)g&n}VG;bTW?iTjl!icOW#nqzwn8|1$HEQ0nGcoTYAbL_ zp}gJuIbIEtRF<}8u^yPgYKLMZC}s;$I}Gt2Ms7a=3LGL|(-p{Gocn~~`)Hbde2PHO z&A4(IQ;xw{IxY;WaA(E$61CN&U`1Ne8nh2wzLdxJvbVn2te%OnZ6a;(Cf{RJB$FEMur?&Z)?+q8bM3lW7@My;%+& zT9TlJu{h!}>9sFiuOYA3mr9H0zLu}wIWjDPz9?C_Cfj~+*%9)bdtCxbgAv&4V%+jA z&8i`q%=biSFN<*}c4>ddC4q}lWS;GS$rjYBp!rHxDjT)-+{vwZ4HHl*C>M$^C$IQBm| zrK&XUu{GEF?h0s^)~dA!tWP8j9Do#Tx5mBICy^o$13QJ`g@#YhaprVg2B)H6o2v6 z87SBR%yHUo0c&faSIN5KXBep;s|`m5?v2<5y+MxsI~eDDkzxwA{yZUZdl(G+K*arc z2%mAwjjd^>_ai;gtc_ByjE&|AhaR0G&bZ|)NwgW6D8%FS(z^k(j{eCv#JD81!(BUd*-xuMYN_;(w z7TZ`A%ng(1k%qZ#Eb2GrG0dBh!^7d^4~Bok`-j7~42SO<4)@|_zDU1Na0*qGRUOqw z(MD*Fc1GGNh)PY1o=Rnl@dcw^LN;!@;l;PIE79_K);Oh1Y;~ zmHSE_qh^X4IZS5wH&S_Qr@<}VnVY!^k?i+WSW=$4JIRy&hguTqgJ7cxL$^%hU?X%w zP4}4bER$-8EdxzgGn1N#9t6C+uV<6Ih#Y@?K)Ib21=A8bx@XYr;1;FX;T7^O3fiZ6 z1jL2t-F(sV)$gq=Y0Yvi)8hbK3QC#kocX@y9 zwj<7WL8JSnuaMvQn0EZmFCBo3ma-%9P4tm#x0~zh--s_E@0@(?+I4dMf!4Lx_9o9= zJIb=rwQ#WX67s|2@k=JJ?k#^UJ23g$ZVjs*tNLFLGVl4C=6QVhZLlnl6fP+oFPtu1 zRk&K=N)ha52QF4m_R%LSbGZ^ zla7k#f7~K4B+e!}CdelUCnP6AD0(TtIJh}>I-okrI|4hLJg`1GK5#y^KS)2ELV!aI zL%2i$M1n-#MQ%n=Myf{+N18|iNSaABN$yGzEK2fA7E7Q^5KMeps9OeG)?5l)C|qP* dl3dtcP+oRlYG6iShG8~gYGKM^bh99gC Date: Wed, 15 Jun 2022 10:15:54 -0400 Subject: [PATCH 040/175] Fix 1ds appender (#152198) --- src/vs/platform/telemetry/node/1dsAppender.ts | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/vs/platform/telemetry/node/1dsAppender.ts b/src/vs/platform/telemetry/node/1dsAppender.ts index aab96b0c1b2..be4ae256cb1 100644 --- a/src/vs/platform/telemetry/node/1dsAppender.ts +++ b/src/vs/platform/telemetry/node/1dsAppender.ts @@ -79,8 +79,7 @@ async function getClient(instrumentationKey: string): Promise { export class OneDataSystemAppender implements ITelemetryAppender { - private _aiCore: AppInsightsCore | undefined; - private _iKey: string | undefined; + private _aiCoreOrKey: AppInsightsCore | string | undefined; private _asyncAiCore: Promise | null; constructor( @@ -93,29 +92,28 @@ export class OneDataSystemAppender implements ITelemetryAppender { } if (typeof iKeyOrClientFactory === 'function') { - this._aiCore = iKeyOrClientFactory(); + this._aiCoreOrKey = iKeyOrClientFactory(); } else { - this._iKey = iKeyOrClientFactory; + this._aiCoreOrKey = iKeyOrClientFactory; } this._asyncAiCore = null; } private _withAIClient(callback: (aiCore: AppInsightsCore) => void): void { - if (!this._aiCore) { + if (!this._aiCoreOrKey) { return; } - if (this._aiCore) { - callback(this._aiCore); + if (typeof this._aiCoreOrKey !== 'string') { + callback(this._aiCoreOrKey); return; } - if (this._iKey && !this._asyncAiCore) { - this._asyncAiCore = getClient(this._iKey); - this._iKey = undefined; + if (!this._asyncAiCore) { + this._asyncAiCore = getClient(this._aiCoreOrKey); } - this._asyncAiCore?.then( + this._asyncAiCore.then( (aiClient) => { callback(aiClient); }, @@ -127,7 +125,7 @@ export class OneDataSystemAppender implements ITelemetryAppender { } log(eventName: string, data?: any): void { - if (!this._aiCore) { + if (!this._aiCoreOrKey) { return; } data = mixin(data, this._defaultData); @@ -144,11 +142,11 @@ export class OneDataSystemAppender implements ITelemetryAppender { } flush(): Promise { - if (this._aiCore) { + if (this._aiCoreOrKey) { return new Promise(resolve => { this._withAIClient((aiClient) => { aiClient.unload(true, () => { - this._aiCore = undefined; + this._aiCoreOrKey = undefined; resolve(undefined); }); }); From 6e1ea79174dd791a0db49c96a9b986ae44823306 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 15 Jun 2022 16:39:08 +0200 Subject: [PATCH 041/175] Only show comment thread range when the comment is expanded (#152206) Fixes #152067 --- src/vs/editor/common/languages.ts | 2 +- .../api/browser/mainThreadComments.ts | 8 +-- .../browser/commentThreadRangeDecorator.ts | 57 +++++++++++++++---- .../browser/commentThreadZoneWidget.ts | 2 +- 4 files changed, 52 insertions(+), 17 deletions(-) diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index bb5e3134ec5..97394a88515 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -1549,7 +1549,7 @@ export interface CommentThread { onDidChangeInput: Event; onDidChangeRange: Event; onDidChangeLabel: Event; - onDidChangeCollasibleState: Event; + onDidChangeCollapsibleState: Event; onDidChangeState: Event; onDidChangeCanReply: Event; isDisposed: boolean; diff --git a/src/vs/workbench/api/browser/mainThreadComments.ts b/src/vs/workbench/api/browser/mainThreadComments.ts index 906c30767f7..7184b593f3a 100644 --- a/src/vs/workbench/api/browser/mainThreadComments.ts +++ b/src/vs/workbench/api/browser/mainThreadComments.ts @@ -110,11 +110,11 @@ export class MainThreadCommentThread implements languages.CommentThread { set collapsibleState(newState: languages.CommentThreadCollapsibleState | undefined) { this._collapsibleState = newState; - this._onDidChangeCollasibleState.fire(this._collapsibleState); + this._onDidChangeCollapsibleState.fire(this._collapsibleState); } - private readonly _onDidChangeCollasibleState = new Emitter(); - public onDidChangeCollasibleState = this._onDidChangeCollasibleState.event; + private readonly _onDidChangeCollapsibleState = new Emitter(); + public onDidChangeCollapsibleState = this._onDidChangeCollapsibleState.event; private _isDisposed: boolean; @@ -172,7 +172,7 @@ export class MainThreadCommentThread implements languages.CommentThread { dispose() { this._isDisposed = true; - this._onDidChangeCollasibleState.dispose(); + this._onDidChangeCollapsibleState.dispose(); this._onDidChangeComments.dispose(); this._onDidChangeInput.dispose(); this._onDidChangeLabel.dispose(); diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadRangeDecorator.ts b/src/vs/workbench/contrib/comments/browser/commentThreadRangeDecorator.ts index 614402762c1..73cdeb596fd 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadRangeDecorator.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadRangeDecorator.ts @@ -3,9 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IRange } from 'vs/editor/common/core/range'; +import { CommentThread, CommentThreadCollapsibleState } from 'vs/editor/common/languages'; import { IModelDecorationOptions, IModelDeltaDecoration } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { ICommentInfo, ICommentService } from 'vs/workbench/contrib/comments/browser/commentService'; @@ -34,6 +35,8 @@ export class CommentThreadRangeDecorator extends Disposable { private decorationIds: string[] = []; private activeDecorationIds: string[] = []; private editor: ICodeEditor | undefined; + private threadCollapseStateListeners: IDisposable[] = []; + private currentThreadCollapseStateListener: IDisposable | undefined; constructor(commentService: ICommentService) { super(); @@ -55,19 +58,34 @@ export class CommentThreadRangeDecorator extends Disposable { this.activeDecorationOptions = ModelDecorationOptions.createDynamic(activeDecorationOptions); this._register(commentService.onDidChangeCurrentCommentThread(thread => { - if (!this.editor) { - return; - } - const newDecoration: CommentThreadRangeDecoration[] = []; - if (thread) { - const range = thread.range; - if (!((range.startLineNumber === range.endLineNumber) && (range.startColumn === range.endColumn))) { + this.updateCurrent(thread); + })); + this._register(commentService.onDidUpdateCommentThreads(() => { + this.updateCurrent(undefined); + })); + } + + private updateCurrent(thread: CommentThread | undefined) { + if (!this.editor) { + return; + } + this.currentThreadCollapseStateListener?.dispose(); + const newDecoration: CommentThreadRangeDecoration[] = []; + if (thread) { + const range = thread.range; + if (!((range.startLineNumber === range.endLineNumber) && (range.startColumn === range.endColumn))) { + if (thread.collapsibleState === CommentThreadCollapsibleState.Expanded) { + this.currentThreadCollapseStateListener = thread.onDidChangeCollapsibleState(state => { + if (state === CommentThreadCollapsibleState.Collapsed) { + this.updateCurrent(undefined); + } + }); newDecoration.push(new CommentThreadRangeDecoration(range, this.activeDecorationOptions)); } } - this.activeDecorationIds = this.editor.deltaDecorations(this.activeDecorationIds, newDecoration); - newDecoration.forEach((decoration, index) => decoration.id = this.decorationIds[index]); - })); + } + this.activeDecorationIds = this.editor.deltaDecorations(this.activeDecorationIds, newDecoration); + newDecoration.forEach((decoration, index) => decoration.id = this.decorationIds[index]); } public update(editor: ICodeEditor, commentInfos: ICommentInfo[]) { @@ -75,6 +93,7 @@ export class CommentThreadRangeDecorator extends Disposable { if (!model) { return; } + this.threadCollapseStateListeners.forEach(disposable => disposable.dispose()); this.editor = editor; const commentThreadRangeDecorations: CommentThreadRangeDecoration[] = []; @@ -83,12 +102,22 @@ export class CommentThreadRangeDecorator extends Disposable { if (thread.isDisposed) { return; } + const range = thread.range; // We only want to show a range decoration when there's the range spans either multiple lines // or, when is spans multiple characters on the sample line if ((range.startLineNumber === range.endLineNumber) && (range.startColumn === range.endColumn)) { return; } + + this.threadCollapseStateListeners.push(thread.onDidChangeCollapsibleState(() => { + this.update(editor, commentInfos); + })); + + if (thread.collapsibleState === CommentThreadCollapsibleState.Collapsed) { + return; + } + commentThreadRangeDecorations.push(new CommentThreadRangeDecoration(range, this.decorationOptions)); }); } @@ -96,4 +125,10 @@ export class CommentThreadRangeDecorator extends Disposable { this.decorationIds = editor.deltaDecorations(this.decorationIds, commentThreadRangeDecorations); commentThreadRangeDecorations.forEach((decoration, index) => decoration.id = this.decorationIds[index]); } + + override dispose() { + this.threadCollapseStateListeners.forEach(disposable => disposable.dispose()); + this.currentThreadCollapseStateListener?.dispose(); + super.dispose(); + } } diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts index c9c6c215711..32b36c7f42f 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts @@ -342,7 +342,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } })); - this._commentThreadDisposables.push(this._commentThread.onDidChangeCollasibleState(state => { + this._commentThreadDisposables.push(this._commentThread.onDidChangeCollapsibleState(state => { if (state === languages.CommentThreadCollapsibleState.Expanded && !this._isExpanded) { const lineNumber = this._commentThread.range.startLineNumber; From c64dd2e8e53fa6382b4cfa011bb13ebf5ddb271d Mon Sep 17 00:00:00 2001 From: "Babak K. Shandiz" Date: Wed, 15 Jun 2022 14:45:30 +0000 Subject: [PATCH 042/175] =?UTF-8?q?=E2=9A=97=EF=B8=8F=20Prevent=20assertio?= =?UTF-8?q?n=20helper=20method=20from=20altering=20args?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Babak K. Shandiz --- .../editor/contrib/snippet/test/browser/snippetParser.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts index 96810968961..13533bed041 100644 --- a/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts +++ b/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts @@ -97,12 +97,12 @@ suite('SnippetParser', () => { function assertMarker(input: TextmateSnippet | Marker[] | string, ...ctors: Function[]) { let marker: Marker[]; if (input instanceof TextmateSnippet) { - marker = input.children; + marker = [...input.children]; } else if (typeof input === 'string') { const p = new SnippetParser(); marker = p.parse(input).children; } else { - marker = input; + marker = [...input]; } while (marker.length > 0) { const m = marker.pop(); From e2d75fb98bef0acd5cbd1ae71e405fe1923be27c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 15 Jun 2022 16:50:01 +0200 Subject: [PATCH 043/175] Clean up outdated builtin extensions (#152207) Fix #151002 --- .../common/extensionsScannerService.ts | 82 +++++++++++++------ .../node/extensionManagementService.ts | 28 +++++-- .../extensions/common/extensionsUtil.ts | 7 +- .../cachedExtensionScanner.ts | 12 +-- 4 files changed, 85 insertions(+), 44 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts index c54fe1b2a34..2c453505e19 100644 --- a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts +++ b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts @@ -173,7 +173,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem this.scanUserExtensions(userScanOptions), ]); const development = await this.scanExtensionsUnderDevelopment(systemScanOptions, [...system, ...user]); - return this.dedupExtensions([...system, ...user, ...development], await this.getTargetPlatform(), true); + return this.dedupExtensions(system, user, development, await this.getTargetPlatform(), true); } async scanSystemExtensions(scanOptions: ScanOptions): Promise { @@ -181,7 +181,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem promises.push(this.scanDefaultSystemExtensions(!!scanOptions.useCache, scanOptions.language)); promises.push(this.scanDevSystemExtensions(scanOptions.language, !!scanOptions.checkControlFile)); const [defaultSystemExtensions, devSystemExtensions] = await Promise.all(promises); - return this.applyScanOptions([...defaultSystemExtensions, ...devSystemExtensions], scanOptions, false); + return this.applyScanOptions([...defaultSystemExtensions, ...devSystemExtensions], ExtensionType.System, scanOptions, false); } async scanUserExtensions(scanOptions: ScanOptions): Promise { @@ -189,7 +189,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem const extensionsScannerInput = await this.createExtensionScannerInput(scanOptions.profileLocation ?? this.userExtensionsLocation, !!scanOptions.profileLocation, ExtensionType.User, !scanOptions.includeUninstalled, scanOptions.language); const extensionsScanner = scanOptions.useCache && !extensionsScannerInput.devMode && extensionsScannerInput.excludeObsolete ? this.userExtensionsCachedScanner : this.extensionsScanner; let extensions = await extensionsScanner.scanExtensions(extensionsScannerInput); - extensions = await this.applyScanOptions(extensions, scanOptions, true); + extensions = await this.applyScanOptions(extensions, ExtensionType.User, scanOptions, true); this.logService.trace('Scanned user extensions:', extensions.length); return extensions; } @@ -208,7 +208,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem }); }))) .flat(); - return this.applyScanOptions(extensions, scanOptions, true); + return this.applyScanOptions(extensions, 'development', scanOptions, true); } return []; } @@ -228,7 +228,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem async scanOneOrMultipleExtensions(extensionLocation: URI, extensionType: ExtensionType, scanOptions: ScanOptions): Promise { const extensionsScannerInput = await this.createExtensionScannerInput(extensionLocation, false, extensionType, true, scanOptions.language); const extensions = await this.extensionsScanner.scanOneOrMultipleExtensions(extensionsScannerInput); - return this.applyScanOptions(extensions, scanOptions, true); + return this.applyScanOptions(extensions, extensionType, scanOptions, true); } async scanMetadata(extensionLocation: URI): Promise { @@ -252,9 +252,9 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem await this.fileService.writeFile(joinPath(extensionLocation, 'package.json'), VSBuffer.fromString(JSON.stringify(manifest, null, '\t'))); } - private async applyScanOptions(extensions: IRelaxedScannedExtension[], scanOptions: ScanOptions, pickLatest: boolean): Promise { + private async applyScanOptions(extensions: IRelaxedScannedExtension[], type: ExtensionType | 'development', scanOptions: ScanOptions, pickLatest: boolean): Promise { if (!scanOptions.includeAllVersions) { - extensions = this.dedupExtensions(extensions, await this.getTargetPlatform(), pickLatest); + extensions = this.dedupExtensions(type === ExtensionType.System ? extensions : undefined, type === ExtensionType.User ? extensions : undefined, type === 'development' ? extensions : undefined, await this.getTargetPlatform(), pickLatest); } if (!scanOptions.includeInvalid) { extensions = extensions.filter(extension => extension.isValid); @@ -272,33 +272,61 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem }); } - private dedupExtensions(extensions: IRelaxedScannedExtension[], targetPlatform: TargetPlatform, pickLatest: boolean): IRelaxedScannedExtension[] { - const result = new Map(); - for (const extension of extensions) { - const extensionKey = ExtensionIdentifier.toKey(extension.identifier.id); - const existing = result.get(extensionKey); - if (existing) { - if (existing.isValid && !extension.isValid) { - continue; + private dedupExtensions(system: IScannedExtension[] | undefined, user: IScannedExtension[] | undefined, development: IScannedExtension[] | undefined, targetPlatform: TargetPlatform, pickLatest: boolean): IScannedExtension[] { + const pick = (existing: IScannedExtension, extension: IScannedExtension): boolean => { + if (existing.isValid && !extension.isValid) { + return false; + } + if (existing.isValid === extension.isValid) { + if (pickLatest && semver.gt(existing.manifest.version, extension.manifest.version)) { + this.logService.debug(`Skipping extension ${extension.location.path} with lower version ${extension.manifest.version} in favour of ${existing.location.path} with version ${existing.manifest.version}`); + return false; } - if (existing.isValid === extension.isValid) { - if (pickLatest && semver.gt(existing.manifest.version, extension.manifest.version)) { - this.logService.debug(`Skipping extension ${extension.location.path} with lower version ${extension.manifest.version}.`); - continue; + if (semver.eq(existing.manifest.version, extension.manifest.version)) { + if (existing.type === ExtensionType.System) { + this.logService.debug(`Skipping extension ${extension.location.path} in favour of system extension ${existing.location.path} with same version`); + return false; } - if (semver.eq(existing.manifest.version, extension.manifest.version) && existing.targetPlatform === targetPlatform) { + if (existing.targetPlatform === targetPlatform) { this.logService.debug(`Skipping extension ${extension.location.path} from different target platform ${extension.targetPlatform}`); - continue; + return false; } } - if (existing.type === ExtensionType.System) { - this.logService.debug(`Overwriting system extension ${existing.location.path} with ${extension.location.path}.`); - } else { - this.logService.warn(`Overwriting user extension ${existing.location.path} with ${extension.location.path}.`); - } + } + if (existing.type === ExtensionType.System) { + this.logService.debug(`Overwriting system extension ${existing.location.path} with ${extension.location.path}.`); + } else { + this.logService.warn(`Overwriting user extension ${existing.location.path} with ${extension.location.path}.`); + } + return true; + }; + const result = new Map(); + system?.forEach((extension) => { + const extensionKey = ExtensionIdentifier.toKey(extension.identifier.id); + const existing = result.get(extensionKey); + if (!existing || pick(existing, extension)) { + result.set(extensionKey, extension); + } + }); + user?.forEach((extension) => { + const extensionKey = ExtensionIdentifier.toKey(extension.identifier.id); + const existing = result.get(extensionKey); + if (!existing && system && extension.type === ExtensionType.System) { + this.logService.debug(`Skipping obsolete system extension ${extension.location.path}.`); + return; + } + if (!existing || pick(existing, extension)) { + result.set(extensionKey, extension); + } + }); + development?.forEach(extension => { + const extensionKey = ExtensionIdentifier.toKey(extension.identifier.id); + const existing = result.get(extensionKey); + if (!existing || pick(existing, extension)) { + result.set(extensionKey, extension); } result.set(extensionKey, extension); - } + }); return [...result.values()]; } diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 1006bd05439..a15de91f394 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -432,23 +432,33 @@ class ExtensionsScanner extends Disposable { } private async removeOutdatedExtensions(): Promise { + const systemExtensions = await this.extensionsScannerService.scanSystemExtensions({}); // System extensions const extensions = await this.extensionsScannerService.scanUserExtensions({ includeAllVersions: true, includeUninstalled: true, includeInvalid: true }); // All user extensions const toRemove: IScannedExtension[] = []; // Outdated extensions const targetPlatform = await this.extensionsScannerService.getTargetPlatform(); const byExtension = groupByExtension(extensions, e => e.identifier); - toRemove.push(...byExtension.map(p => p.sort((a, b) => { - const vcompare = semver.rcompare(a.manifest.version, b.manifest.version); - if (vcompare !== 0) { - return vcompare; + for (const extensions of byExtension) { + if (extensions.length > 1) { + toRemove.push(...extensions.sort((a, b) => { + const vcompare = semver.rcompare(a.manifest.version, b.manifest.version); + if (vcompare !== 0) { + return vcompare; + } + if (a.targetPlatform === targetPlatform) { + return -1; + } + return 1; + }).slice(1)); } - if (a.targetPlatform === targetPlatform) { - return -1; + if (extensions[0].type === ExtensionType.System) { + const systemExtension = systemExtensions.find(e => areSameExtensions(e.identifier, extensions[0].identifier)); + if (!systemExtension || semver.gte(systemExtension.manifest.version, extensions[0].manifest.version)) { + toRemove.push(extensions[0]); + } } - return 1; - }).slice(1)).flat()); - + } await Promises.settled(toRemove.map(extension => this.removeExtension(extension, 'outdated'))); } diff --git a/src/vs/workbench/services/extensions/common/extensionsUtil.ts b/src/vs/workbench/services/extensions/common/extensionsUtil.ts index c1755f15dff..0a65c761e81 100644 --- a/src/vs/workbench/services/extensions/common/extensionsUtil.ts +++ b/src/vs/workbench/services/extensions/common/extensionsUtil.ts @@ -24,8 +24,8 @@ export function dedupExtensions(system: IExtensionDescription[], user: IExtensio const extension = result.get(extensionKey); if (extension) { if (extension.isBuiltin) { - if (semver.gt(extension.version, userExtension.version)) { - logService.warn(`Skipping extension ${userExtension.extensionLocation.path} with lower version ${userExtension.version}.`); + if (semver.gte(extension.version, userExtension.version)) { + logService.warn(`Skipping extension ${userExtension.extensionLocation.path} in favour of the builtin extension ${extension.extensionLocation.path}.`); return; } // Overwriting a builtin extension inherits the `isBuiltin` property and it doesn't show a warning @@ -33,6 +33,9 @@ export function dedupExtensions(system: IExtensionDescription[], user: IExtensio } else { logService.warn(localize('overwritingExtension', "Overwriting extension {0} with {1}.", extension.extensionLocation.fsPath, userExtension.extensionLocation.fsPath)); } + } else if (userExtension.isBuiltin) { + logService.warn(`Skipping obsolete builtin extension ${userExtension.extensionLocation.path}`); + return; } result.set(extensionKey, userExtension); }); diff --git a/src/vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner.ts index 69a2d8f5d53..a38a74255db 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner.ts @@ -43,15 +43,14 @@ export class CachedExtensionScanner { public async startScanningExtensions(): Promise { try { - const { system, user, development } = await this._scanInstalledExtensions(); - const r = dedupExtensions(system, user, development, this._logService); - this._scannedExtensionsResolve(r); + const extensions = await this._scanInstalledExtensions(); + this._scannedExtensionsResolve(extensions); } catch (err) { this._scannedExtensionsReject(err); } } - private async _scanInstalledExtensions(): Promise<{ system: IExtensionDescription[]; user: IExtensionDescription[]; development: IExtensionDescription[] }> { + private async _scanInstalledExtensions(): Promise { try { const language = platform.language; const [scannedSystemExtensions, scannedUserExtensions] = await Promise.all([ @@ -61,6 +60,7 @@ export class CachedExtensionScanner { const system = scannedSystemExtensions.map(e => toExtensionDescription(e, false)); const user = scannedUserExtensions.map(e => toExtensionDescription(e, false)); const development = scannedDevelopedExtensions.map(e => toExtensionDescription(e, true)); + const r = dedupExtensions(system, user, development, this._logService); const disposable = this._extensionsScannerService.onDidChangeCache(() => { disposable.dispose(); this._notificationService.prompt( @@ -73,11 +73,11 @@ export class CachedExtensionScanner { ); }); timeout(5000).then(() => disposable.dispose()); - return { system, user, development }; + return r; } catch (err) { this._logService.error(`Error scanning installed extensions:`); this._logService.error(err); - return { system: [], user: [], development: [] }; + return []; } } From 600bfb5995f0ba64a500d65027d7b10407674a8f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 15 Jun 2022 16:52:04 +0200 Subject: [PATCH 044/175] remove unused code (#152208) --- src/vs/base/browser/dom.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index d8a7ac484c4..c323e74ddfc 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -1685,18 +1685,6 @@ export function getCookieValue(name: string): string | undefined { return match ? match.pop() : undefined; } -export const enum ZIndex { - SASH = 35, - SuggestWidget = 40, - Hover = 50, - DragImage = 1000, - MenubarMenuItemsHolder = 2000, // quick-input-widget - ContextView = 2500, - ModalDialog = 2600, - PaneDropOverlay = 10000 -} - - export interface IDragAndDropObserverCallbacks { readonly onDragEnter: (e: DragEvent) => void; readonly onDragLeave: (e: DragEvent) => void; From 9e21aff42e30b602108dc104fd5c7c8edd2c5b82 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 15 Jun 2022 16:52:48 +0200 Subject: [PATCH 045/175] joh/ts transpile (#152199) transpile-only tasks for client and extensions * extract transpile into its own file * add transpile-client task, polish transpiler * add transpile-extensions, improve transpile logic * move declaration of "const enum" above it usage so that it can be used with const-enum-inlining * (ugly) make d.ts transpilation configurable because it is needed for extensions but a problem for client * hack my way around so that `getOwnEmitOutputFilePath` is reusable by our transpile * honor `noEmit` flag --- build/gulpfile.compile.js | 2 +- build/gulpfile.editor.js | 2 +- build/gulpfile.extensions.js | 20 +- build/gulpfile.js | 6 +- build/lib/compilation.js | 13 +- build/lib/compilation.ts | 13 +- build/lib/tsb/index.js | 49 ++-- build/lib/tsb/index.ts | 62 ++-- build/lib/tsb/transpiler.js | 210 +++++++++++++ build/lib/tsb/transpiler.ts | 276 ++++++++++++++++++ extensions/debug-auto-launch/src/extension.ts | 12 +- 11 files changed, 581 insertions(+), 84 deletions(-) create mode 100644 build/lib/tsb/transpiler.js create mode 100644 build/lib/tsb/transpiler.ts diff --git a/build/gulpfile.compile.js b/build/gulpfile.compile.js index c3049784fe4..6f4523eeae6 100644 --- a/build/gulpfile.compile.js +++ b/build/gulpfile.compile.js @@ -15,7 +15,7 @@ const compileBuildTask = task.define('compile-build', task.series( util.rimraf('out-build'), util.buildWebNodePaths('out-build'), - compilation.compileTask('src', 'out-build', true) + compilation.compileTask('src', 'out-build', true, false) ) ); gulp.task(compileBuildTask); diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 3b3fe4942d8..45546aa9e64 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -78,7 +78,7 @@ const extractEditorSrcTask = task.define('extract-editor-src', () => { }); }); -const compileEditorAMDTask = task.define('compile-editor-amd', compilation.compileTask('out-editor-src', 'out-editor-build', true)); +const compileEditorAMDTask = task.define('compile-editor-amd', compilation.compileTask('out-editor-src', 'out-editor-build', true, false)); const optimizeEditorAMDTask = task.define('optimize-editor-amd', common.optimizeTask({ src: 'out-editor-build', diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index 3f9f19b4b94..71fa1ec425c 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -100,7 +100,7 @@ const tasks = compilations.map(function (tsconfigFile) { headerOut = relativeDirname.substr(index + 1) + '/out'; } - function createPipeline(build, emitError) { + function createPipeline(build, emitError, transpileOnly) { const nlsDev = require('vscode-nls-dev'); const tsb = require('./lib/tsb'); const sourcemaps = require('gulp-sourcemaps'); @@ -110,7 +110,7 @@ const tasks = compilations.map(function (tsconfigFile) { overrideOptions.inlineSources = Boolean(build); overrideOptions.base = path.dirname(absolutePath); - const compilation = tsb.create(absolutePath, overrideOptions, { verbose: false }, err => reporter(err.toString())); + const compilation = tsb.create(absolutePath, overrideOptions, { verbose: false, transpileOnly, transpileOnlyIncludesDts: transpileOnly }, err => reporter(err.toString())); const pipeline = function () { const input = es.through(); @@ -152,6 +152,16 @@ const tasks = compilations.map(function (tsconfigFile) { const cleanTask = task.define(`clean-extension-${name}`, util.rimraf(out)); + const transpileTask = task.define(`transpile-extension:${name}`, task.series(cleanTask, () => { + const pipeline = createPipeline(false, true, true); + const nonts = gulp.src(src, srcOpts).pipe(filter(['**', '!**/*.ts'])); + const input = es.merge(nonts, pipeline.tsProjectSrc()); + + return input + .pipe(pipeline()) + .pipe(gulp.dest(out)); + })); + const compileTask = task.define(`compile-extension:${name}`, task.series(cleanTask, () => { const pipeline = createPipeline(false, true); const nonts = gulp.src(src, srcOpts).pipe(filter(['**', '!**/*.ts'])); @@ -184,12 +194,16 @@ const tasks = compilations.map(function (tsconfigFile) { })); // Tasks + gulp.task(transpileTask); gulp.task(compileTask); gulp.task(watchTask); - return { compileTask, watchTask, compileBuildTask }; + return { transpileTask, compileTask, watchTask, compileBuildTask }; }); +const transpileExtensionsTask = task.define('transpile-extensions', task.parallel(...tasks.map(t => t.transpileTask))); +gulp.task(transpileExtensionsTask); + const compileExtensionsTask = task.define('compile-extensions', task.parallel(...tasks.map(t => t.compileTask))); gulp.task(compileExtensionsTask); exports.compileExtensionsTask = compileExtensionsTask; diff --git a/build/gulpfile.js b/build/gulpfile.js index 65dda505fa7..75d3755d8e5 100644 --- a/build/gulpfile.js +++ b/build/gulpfile.js @@ -19,8 +19,12 @@ const { compileExtensionsTask, watchExtensionsTask, compileExtensionMediaTask } gulp.task(compileApiProposalNamesTask); gulp.task(watchApiProposalNamesTask); +// Transpile only +const transpileClientTask = task.define('transpile-client', task.series(util.rimraf('out'), util.buildWebNodePaths('out'), compileTask('src', 'out', false, true))); +gulp.task(transpileClientTask); + // Fast compile for development time -const compileClientTask = task.define('compile-client', task.series(util.rimraf('out'), util.buildWebNodePaths('out'), compileApiProposalNamesTask, compileTask('src', 'out', false))); +const compileClientTask = task.define('compile-client', task.series(util.rimraf('out'), util.buildWebNodePaths('out'), compileApiProposalNamesTask, compileTask('src', 'out', false, false))); gulp.task(compileClientTask); const watchClientTask = task.define('watch-client', task.series(util.rimraf('out'), util.buildWebNodePaths('out'), task.parallel(watchTask('out', false), watchApiProposalNamesTask))); diff --git a/build/lib/compilation.js b/build/lib/compilation.js index 2c815b1ad9b..d21c5b87f36 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -18,6 +18,7 @@ const ansiColors = require("ansi-colors"); const os = require("os"); const File = require("vinyl"); const task = require("./task"); +const tsb = require("./tsb"); const watch = require('./watch'); const reporter = (0, reporter_1.createReporter)(); function getTypeScriptCompilerOptions(src) { @@ -34,15 +35,15 @@ function getTypeScriptCompilerOptions(src) { options.newLine = /\r\n/.test(fs.readFileSync(__filename, 'utf8')) ? 0 : 1; return options; } -function createCompile(src, build, emitError) { - const tsb = require('./tsb'); +function createCompile(src, build, emitError, transpileOnly) { + // const tsb = require('./tsb') as typeof import('./tsb'); const sourcemaps = require('gulp-sourcemaps'); const projectPath = path.join(__dirname, '../../', src, 'tsconfig.json'); const overrideOptions = { ...getTypeScriptCompilerOptions(src), inlineSources: Boolean(build) }; if (!build) { overrideOptions.inlineSourceMap = true; } - const compilation = tsb.create(projectPath, overrideOptions, { verbose: false }, err => reporter(err)); + const compilation = tsb.create(projectPath, overrideOptions, { verbose: false, transpileOnly }, err => reporter(err)); function pipeline(token) { const bom = require('gulp-bom'); const utf8Filter = util.filter(data => /(\/|\\)test(\/|\\).*utf8/.test(data.path)); @@ -73,12 +74,12 @@ function createCompile(src, build, emitError) { }; return pipeline; } -function compileTask(src, out, build) { +function compileTask(src, out, build, transpileOnly) { return function () { if (os.totalmem() < 4000000000) { throw new Error('compilation requires 4GB of RAM'); } - const compile = createCompile(src, build, true); + const compile = createCompile(src, build, true, transpileOnly); const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); const generator = new MonacoGenerator(false); if (src === 'src') { @@ -93,7 +94,7 @@ function compileTask(src, out, build) { exports.compileTask = compileTask; function watchTask(out, build) { return function () { - const compile = createCompile('src', build); + const compile = createCompile('src', build, false, false); const src = gulp.src('src/**', { base: 'src' }); const watchSrc = watch('src/**', { base: 'src', readDelay: 200 }); const generator = new MonacoGenerator(true); diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index 0cb12f4ad53..c72f7fdecda 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -19,6 +19,7 @@ import * as os from 'os'; import ts = require('typescript'); import * as File from 'vinyl'; import * as task from './task'; +import * as tsb from './tsb'; const watch = require('./watch'); @@ -39,8 +40,8 @@ function getTypeScriptCompilerOptions(src: string): ts.CompilerOptions { return options; } -function createCompile(src: string, build: boolean, emitError?: boolean) { - const tsb = require('./tsb') as typeof import('./tsb'); +function createCompile(src: string, build: boolean, emitError: boolean, transpileOnly: boolean) { + // const tsb = require('./tsb') as typeof import('./tsb'); const sourcemaps = require('gulp-sourcemaps') as typeof import('gulp-sourcemaps'); @@ -50,7 +51,7 @@ function createCompile(src: string, build: boolean, emitError?: boolean) { overrideOptions.inlineSourceMap = true; } - const compilation = tsb.create(projectPath, overrideOptions, { verbose: false }, err => reporter(err)); + const compilation = tsb.create(projectPath, overrideOptions, { verbose: false, transpileOnly }, err => reporter(err)); function pipeline(token?: util.ICancellationToken) { const bom = require('gulp-bom') as typeof import('gulp-bom'); @@ -86,7 +87,7 @@ function createCompile(src: string, build: boolean, emitError?: boolean) { return pipeline; } -export function compileTask(src: string, out: string, build: boolean): () => NodeJS.ReadWriteStream { +export function compileTask(src: string, out: string, build: boolean, transpileOnly: boolean): () => NodeJS.ReadWriteStream { return function () { @@ -94,7 +95,7 @@ export function compileTask(src: string, out: string, build: boolean): () => Nod throw new Error('compilation requires 4GB of RAM'); } - const compile = createCompile(src, build, true); + const compile = createCompile(src, build, true, transpileOnly); const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); const generator = new MonacoGenerator(false); if (src === 'src') { @@ -111,7 +112,7 @@ export function compileTask(src: string, out: string, build: boolean): () => Nod export function watchTask(out: string, build: boolean): () => NodeJS.ReadWriteStream { return function () { - const compile = createCompile('src', build); + const compile = createCompile('src', build, false, false); const src = gulp.src('src/**', { base: 'src' }); const watchSrc = watch('src/**', { base: 'src', readDelay: 200 }); diff --git a/build/lib/tsb/index.js b/build/lib/tsb/index.js index 44b9bc862dd..adab557fe87 100644 --- a/build/lib/tsb/index.js +++ b/build/lib/tsb/index.js @@ -15,6 +15,7 @@ const utils_1 = require("./utils"); const fs_1 = require("fs"); const log = require("fancy-log"); const colors = require("ansi-colors"); +const transpiler_1 = require("./transpiler"); class EmptyDuplex extends stream_1.Duplex { _write(_chunk, _encoding, callback) { callback(); } _read() { this.push(null); } @@ -51,25 +52,21 @@ function create(projectPath, existingOptions, config, onError = _defaultOnError) } } // FULL COMPILE stream doing transpile, syntax and semantic diagnostics - let _builder; - function createCompileStream(token) { - if (!_builder) { - _builder = builder.createTypeScriptBuilder({ logFn }, projectPath, cmdLine); - } + function createCompileStream(builder, token) { return through(function (file) { // give the file to the compiler if (file.isStream()) { this.emit('error', 'no support for streams'); return; } - _builder.file(file); + builder.file(file); }, function () { // start the compilation process - _builder.build(file => this.queue(file), printDiagnostic, token).catch(e => console.error(e)).then(() => this.queue(null)); + builder.build(file => this.queue(file), printDiagnostic, token).catch(e => console.error(e)).then(() => this.queue(null)); }); } // TRANSPILE ONLY stream doing just TS to JS conversion - function createTranspileStream() { + function createTranspileStream(transpiler) { return through(function (file) { // give the file to the compiler if (file.isStream()) { @@ -79,27 +76,29 @@ function create(projectPath, existingOptions, config, onError = _defaultOnError) if (!file.contents) { return; } - const out = ts.transpileModule(String(file.contents), { - compilerOptions: { ...cmdLine.options, declaration: false, sourceMap: false } - }); - if (out.diagnostics) { - out.diagnostics.forEach(printDiagnostic); + if (!config.transpileOnlyIncludesDts && file.path.endsWith('.d.ts')) { + return; } - const outFile = new Vinyl({ - path: file.path.replace(/\.ts$/, '.js'), - cwd: file.cwd, - base: file.base, - contents: Buffer.from(out.outputText), + if (!transpiler.onOutfile) { + transpiler.onOutfile = file => this.queue(file); + } + transpiler.transpile(file); + }, function () { + transpiler.join().then(() => { + this.queue(null); + transpiler.onOutfile = undefined; }); - this.push(outFile); - logFn('Transpiled', file.path); }); } - const result = (token) => { - return config.transplileOnly - ? createTranspileStream() - : createCompileStream(token); - }; + let result; + if (config.transpileOnly) { + const transpiler = new transpiler_1.Transpiler(logFn, printDiagnostic, cmdLine); + result = (() => createTranspileStream(transpiler)); + } + else { + const _builder = builder.createTypeScriptBuilder({ logFn }, projectPath, cmdLine); + result = ((token) => createCompileStream(_builder, token)); + } result.src = (opts) => { let _pos = 0; const _fileNames = cmdLine.fileNames.slice(0); diff --git a/build/lib/tsb/index.ts b/build/lib/tsb/index.ts index 548094bbf03..1164efc9d03 100644 --- a/build/lib/tsb/index.ts +++ b/build/lib/tsb/index.ts @@ -13,6 +13,7 @@ import { strings } from './utils'; import { readFileSync, statSync } from 'fs'; import * as log from 'fancy-log'; import colors = require('ansi-colors'); +import { Transpiler } from './transpiler'; export interface IncrementalCompiler { (token?: any): Readable & Writable; @@ -35,7 +36,7 @@ const _defaultOnError = (err: string) => console.log(JSON.stringify(err, null, 4 export function create( projectPath: string, existingOptions: Partial, - config: { verbose?: boolean; transplileOnly?: boolean }, + config: { verbose?: boolean; transpileOnly?: boolean; transpileOnlyIncludesDts?: boolean }, onError: (message: string) => void = _defaultOnError ): IncrementalCompiler { @@ -73,13 +74,7 @@ export function create( } // FULL COMPILE stream doing transpile, syntax and semantic diagnostics - - let _builder!: builder.ITypeScriptBuilder; - function createCompileStream(token?: builder.CancellationToken): Readable & Writable { - - if (!_builder) { - _builder = builder.createTypeScriptBuilder({ logFn }, projectPath, cmdLine); - } + function createCompileStream(builder: builder.ITypeScriptBuilder, token?: builder.CancellationToken): Readable & Writable { return through(function (this: through.ThroughStream, file: Vinyl) { // give the file to the compiler @@ -87,11 +82,11 @@ export function create( this.emit('error', 'no support for streams'); return; } - _builder.file(file); + builder.file(file); }, function (this: { queue(a: any): void }) { // start the compilation process - _builder.build( + builder.build( file => this.queue(file), printDiagnostic, token @@ -100,46 +95,43 @@ export function create( } // TRANSPILE ONLY stream doing just TS to JS conversion - function createTranspileStream(): Readable & Writable { - - return through(function (this: through.ThroughStream, file: Vinyl) { + function createTranspileStream(transpiler: Transpiler): Readable & Writable { + return through(function (this: through.ThroughStream & { queue(a: any): void }, file: Vinyl) { // give the file to the compiler if (file.isStream()) { this.emit('error', 'no support for streams'); return; } - if (!file.contents) { return; } - - const out = ts.transpileModule(String(file.contents), { - compilerOptions: { ...cmdLine.options, declaration: false, sourceMap: false } - }); - - if (out.diagnostics) { - out.diagnostics.forEach(printDiagnostic); + if (!config.transpileOnlyIncludesDts && file.path.endsWith('.d.ts')) { + return; } - const outFile = new Vinyl({ - path: file.path.replace(/\.ts$/, '.js'), - cwd: file.cwd, - base: file.base, - contents: Buffer.from(out.outputText), + if (!transpiler.onOutfile) { + transpiler.onOutfile = file => this.queue(file); + } + + transpiler.transpile(file); + + }, function (this: { queue(a: any): void }) { + transpiler.join().then(() => { + this.queue(null); + transpiler.onOutfile = undefined; }); - - this.push(outFile); - - logFn('Transpiled', file.path); }); } - const result = (token: builder.CancellationToken) => { - return config.transplileOnly - ? createTranspileStream() - : createCompileStream(token); - }; + let result: IncrementalCompiler; + if (config.transpileOnly) { + const transpiler = new Transpiler(logFn, printDiagnostic, cmdLine); + result = (() => createTranspileStream(transpiler)); + } else { + const _builder = builder.createTypeScriptBuilder({ logFn }, projectPath, cmdLine); + result = ((token: builder.CancellationToken) => createCompileStream(_builder, token)); + } result.src = (opts?: { cwd?: string; base?: string }) => { let _pos = 0; diff --git a/build/lib/tsb/transpiler.js b/build/lib/tsb/transpiler.js new file mode 100644 index 00000000000..406075bbf8e --- /dev/null +++ b/build/lib/tsb/transpiler.js @@ -0,0 +1,210 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Transpiler = void 0; +const ts = require("typescript"); +const threads = require("node:worker_threads"); +const Vinyl = require("vinyl"); +const node_os_1 = require("node:os"); +function transpile(tsSrc, options) { + const isAmd = /\n(import|export)/m.test(tsSrc); + if (!isAmd && options.compilerOptions?.module === ts.ModuleKind.AMD) { + // enforce NONE module-system for not-amd cases + options = { ...options, ...{ compilerOptions: { ...options.compilerOptions, module: ts.ModuleKind.None } } }; + } + const out = ts.transpileModule(tsSrc, options); + return { + jsSrc: out.outputText, + diag: out.diagnostics ?? [] + }; +} +if (!threads.isMainThread) { + // WORKER + threads.parentPort?.addListener('message', (req) => { + const res = { + jsSrcs: [], + diagnostics: [] + }; + for (const tsSrc of req.tsSrcs) { + const out = transpile(tsSrc, req.options); + res.jsSrcs.push(out.jsSrc); + res.diagnostics.push(out.diag); + } + threads.parentPort.postMessage(res); + }); +} +class TranspileWorker { + constructor(outFileFn) { + this.id = TranspileWorker.pool++; + this._worker = new threads.Worker(__filename); + this._durations = []; + this._worker.addListener('message', (res) => { + if (!this._pending) { + console.error('RECEIVING data WITHOUT request'); + return; + } + const [resolve, reject, files, options, t1] = this._pending; + const outFiles = []; + const diag = []; + for (let i = 0; i < res.jsSrcs.length; i++) { + // inputs and outputs are aligned across the arrays + const file = files[i]; + const jsSrc = res.jsSrcs[i]; + const diag = res.diagnostics[i]; + if (diag.length > 0) { + diag.push(...diag); + continue; + } + let SuffixTypes; + (function (SuffixTypes) { + SuffixTypes[SuffixTypes["Dts"] = 5] = "Dts"; + SuffixTypes[SuffixTypes["Ts"] = 3] = "Ts"; + SuffixTypes[SuffixTypes["Unknown"] = 0] = "Unknown"; + })(SuffixTypes || (SuffixTypes = {})); + const suffixLen = file.path.endsWith('.d.ts') ? 5 /* SuffixTypes.Dts */ + : file.path.endsWith('.ts') ? 3 /* SuffixTypes.Ts */ + : 0 /* SuffixTypes.Unknown */; + // check if output of a DTS-files isn't just "empty" and iff so + // skip this file + if (suffixLen === 5 /* SuffixTypes.Dts */ && _isDefaultEmpty(jsSrc)) { + continue; + } + const outBase = options.compilerOptions?.outDir ?? file.base; + const outPath = outFileFn(file.path); + outFiles.push(new Vinyl({ + path: outPath, + base: outBase, + contents: Buffer.from(jsSrc), + })); + } + this._pending = undefined; + this._durations.push(Date.now() - t1); + if (diag.length > 0) { + reject(diag); + } + else { + resolve(outFiles); + } + }); + } + terminate() { + // console.log(`Worker#${this.id} ENDS after ${this._durations.length} jobs (total: ${this._durations.reduce((p, c) => p + c, 0)}, avg: ${this._durations.reduce((p, c) => p + c, 0) / this._durations.length})`); + this._worker.terminate(); + } + get isBusy() { + return this._pending !== undefined; + } + next(files, options) { + if (this._pending !== undefined) { + throw new Error('BUSY'); + } + return new Promise((resolve, reject) => { + this._pending = [resolve, reject, files, options, Date.now()]; + const req = { + options, + tsSrcs: files.map(file => String(file.contents)) + }; + this._worker.postMessage(req); + }); + } +} +TranspileWorker.pool = 1; +class Transpiler { + constructor(logFn, _onError, _cmdLine) { + this._onError = _onError; + this._cmdLine = _cmdLine; + this._workerPool = []; + this._queue = []; + this._allJobs = []; + this._tsApiInternalOutfileName = new class { + constructor(parsedCmd) { + const host = ts.createCompilerHost(parsedCmd.options); + const program = ts.createProgram({ options: parsedCmd.options, rootNames: parsedCmd.fileNames, host }); + const emitHost = { + getCompilerOptions: () => parsedCmd.options, + getCurrentDirectory: () => host.getCurrentDirectory(), + getCanonicalFileName: file => host.getCanonicalFileName(file), + getCommonSourceDirectory: () => program.getCommonSourceDirectory() + }; + this.getForInfile = file => { + return ts.getOwnEmitOutputFilePath(file, emitHost, '.js'); + }; + } + }(this._cmdLine); + logFn('Transpile', `will use ${Transpiler.P} transpile worker`); + } + async join() { + // wait for all penindg jobs + this._consumeQueue(); + await Promise.allSettled(this._allJobs); + this._allJobs.length = 0; + // terminate all worker + this._workerPool.forEach(w => w.terminate()); + this._workerPool.length = 0; + } + transpile(file) { + if (this._cmdLine.options.noEmit) { + // not doing ANYTHING here + return; + } + const newLen = this._queue.push(file); + if (newLen > Transpiler.P ** 2) { + this._consumeQueue(); + } + } + _consumeQueue() { + if (this._queue.length === 0) { + // no work... + return; + } + // kinda LAZYily create workers + if (this._workerPool.length === 0) { + for (let i = 0; i < Transpiler.P; i++) { + this._workerPool.push(new TranspileWorker(file => this._tsApiInternalOutfileName.getForInfile(file))); + } + } + const freeWorker = this._workerPool.filter(w => !w.isBusy); + if (freeWorker.length === 0) { + // OK, they will pick up work themselves + return; + } + for (const worker of freeWorker) { + if (this._queue.length === 0) { + break; + } + const job = new Promise(resolve => { + const consume = () => { + const files = this._queue.splice(0, Transpiler.P); + if (files.length === 0) { + // DONE + resolve(undefined); + return; + } + // work on the NEXT file + // const [inFile, outFn] = req; + worker.next(files, { compilerOptions: this._cmdLine.options }).then(outFiles => { + if (this.onOutfile) { + outFiles.map(this.onOutfile, this); + } + consume(); + }).catch(err => { + this._onError(err); + }); + }; + consume(); + }); + this._allJobs.push(job); + } + } +} +exports.Transpiler = Transpiler; +Transpiler.P = Math.floor((0, node_os_1.cpus)().length * .5); +function _isDefaultEmpty(src) { + return src + .replace('"use strict";', '') + .replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '$1') + .trim().length === 0; +} diff --git a/build/lib/tsb/transpiler.ts b/build/lib/tsb/transpiler.ts new file mode 100644 index 00000000000..ad515263f6c --- /dev/null +++ b/build/lib/tsb/transpiler.ts @@ -0,0 +1,276 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as ts from 'typescript'; +import * as threads from 'node:worker_threads'; +import * as Vinyl from 'vinyl'; +import { cpus } from 'node:os'; + +interface TranspileReq { + readonly tsSrcs: string[]; + readonly options: ts.TranspileOptions; +} + +interface TranspileRes { + readonly jsSrcs: string[]; + readonly diagnostics: ts.Diagnostic[][]; +} + +function transpile(tsSrc: string, options: ts.TranspileOptions): { jsSrc: string; diag: ts.Diagnostic[] } { + + const isAmd = /\n(import|export)/m.test(tsSrc); + if (!isAmd && options.compilerOptions?.module === ts.ModuleKind.AMD) { + // enforce NONE module-system for not-amd cases + options = { ...options, ...{ compilerOptions: { ...options.compilerOptions, module: ts.ModuleKind.None } } }; + } + const out = ts.transpileModule(tsSrc, options); + return { + jsSrc: out.outputText, + diag: out.diagnostics ?? [] + }; +} + +if (!threads.isMainThread) { + // WORKER + threads.parentPort?.addListener('message', (req: TranspileReq) => { + const res: TranspileRes = { + jsSrcs: [], + diagnostics: [] + }; + for (const tsSrc of req.tsSrcs) { + const out = transpile(tsSrc, req.options); + res.jsSrcs.push(out.jsSrc); + res.diagnostics.push(out.diag); + } + threads.parentPort!.postMessage(res); + }); +} + +class TranspileWorker { + + private static pool = 1; + + readonly id = TranspileWorker.pool++; + + private _worker = new threads.Worker(__filename); + private _pending?: [resolve: Function, reject: Function, file: Vinyl[], options: ts.TranspileOptions, t1: number]; + private _durations: number[] = []; + + constructor(outFileFn: (fileName: string) => string) { + + this._worker.addListener('message', (res: TranspileRes) => { + if (!this._pending) { + console.error('RECEIVING data WITHOUT request'); + return; + } + + const [resolve, reject, files, options, t1] = this._pending; + + const outFiles: Vinyl[] = []; + const diag: ts.Diagnostic[] = []; + + for (let i = 0; i < res.jsSrcs.length; i++) { + // inputs and outputs are aligned across the arrays + const file = files[i]; + const jsSrc = res.jsSrcs[i]; + const diag = res.diagnostics[i]; + + if (diag.length > 0) { + diag.push(...diag); + continue; + } + const enum SuffixTypes { + Dts = 5, + Ts = 3, + Unknown = 0 + } + const suffixLen = file.path.endsWith('.d.ts') ? SuffixTypes.Dts + : file.path.endsWith('.ts') ? SuffixTypes.Ts + : SuffixTypes.Unknown; + + // check if output of a DTS-files isn't just "empty" and iff so + // skip this file + if (suffixLen === SuffixTypes.Dts && _isDefaultEmpty(jsSrc)) { + continue; + } + + const outBase = options.compilerOptions?.outDir ?? file.base; + const outPath = outFileFn(file.path); + + outFiles.push(new Vinyl({ + path: outPath, + base: outBase, + contents: Buffer.from(jsSrc), + })); + } + + this._pending = undefined; + this._durations.push(Date.now() - t1); + + if (diag.length > 0) { + reject(diag); + } else { + resolve(outFiles); + } + }); + } + + terminate() { + // console.log(`Worker#${this.id} ENDS after ${this._durations.length} jobs (total: ${this._durations.reduce((p, c) => p + c, 0)}, avg: ${this._durations.reduce((p, c) => p + c, 0) / this._durations.length})`); + this._worker.terminate(); + } + + get isBusy() { + return this._pending !== undefined; + } + + next(files: Vinyl[], options: ts.TranspileOptions) { + if (this._pending !== undefined) { + throw new Error('BUSY'); + } + return new Promise((resolve, reject) => { + this._pending = [resolve, reject, files, options, Date.now()]; + const req: TranspileReq = { + options, + tsSrcs: files.map(file => String(file.contents)) + }; + this._worker.postMessage(req); + }); + } +} + + +export class Transpiler { + + static P = Math.floor(cpus().length * .5); + + public onOutfile?: (file: Vinyl) => void; + + private _workerPool: TranspileWorker[] = []; + private _queue: Vinyl[] = []; + private _allJobs: Promise[] = []; + + constructor( + logFn: (topic: string, message: string) => void, + private readonly _onError: (err: any) => void, + private readonly _cmdLine: ts.ParsedCommandLine + ) { + logFn('Transpile', `will use ${Transpiler.P} transpile worker`); + } + + async join() { + // wait for all penindg jobs + this._consumeQueue(); + await Promise.allSettled(this._allJobs); + this._allJobs.length = 0; + + // terminate all worker + this._workerPool.forEach(w => w.terminate()); + this._workerPool.length = 0; + } + + + transpile(file: Vinyl) { + + if (this._cmdLine.options.noEmit) { + // not doing ANYTHING here + return; + } + + const newLen = this._queue.push(file); + if (newLen > Transpiler.P ** 2) { + this._consumeQueue(); + } + } + + private _consumeQueue(): void { + + if (this._queue.length === 0) { + // no work... + return; + } + + // kinda LAZYily create workers + if (this._workerPool.length === 0) { + for (let i = 0; i < Transpiler.P; i++) { + this._workerPool.push(new TranspileWorker(file => this._tsApiInternalOutfileName.getForInfile(file))); + } + } + + const freeWorker = this._workerPool.filter(w => !w.isBusy); + if (freeWorker.length === 0) { + // OK, they will pick up work themselves + return; + } + + for (const worker of freeWorker) { + if (this._queue.length === 0) { + break; + } + + const job = new Promise(resolve => { + + const consume = () => { + const files = this._queue.splice(0, Transpiler.P); + if (files.length === 0) { + // DONE + resolve(undefined); + return; + } + // work on the NEXT file + // const [inFile, outFn] = req; + worker.next(files, { compilerOptions: this._cmdLine.options }).then(outFiles => { + if (this.onOutfile) { + outFiles.map(this.onOutfile, this); + } + consume(); + }).catch(err => { + this._onError(err); + }); + }; + + consume(); + }); + + this._allJobs.push(job); + } + } + + private _tsApiInternalOutfileName = new class { + + getForInfile: (file: string) => string; + + constructor(parsedCmd: ts.ParsedCommandLine) { + + type InternalTsHost = { + getCompilerOptions(): ts.CompilerOptions; + getCurrentDirectory(): string; + getCommonSourceDirectory(): string; + getCanonicalFileName(file: string): string; + }; + type InternalTsApi = { getOwnEmitOutputFilePath(fileName: string, host: InternalTsHost, extension: string): string } & typeof ts; + + const host = ts.createCompilerHost(parsedCmd.options); + const program = ts.createProgram({ options: parsedCmd.options, rootNames: parsedCmd.fileNames, host }); + const emitHost: InternalTsHost = { + getCompilerOptions: () => parsedCmd.options, + getCurrentDirectory: () => host.getCurrentDirectory(), + getCanonicalFileName: file => host.getCanonicalFileName(file), + getCommonSourceDirectory: () => (program).getCommonSourceDirectory() + }; + + this.getForInfile = file => { + return (ts).getOwnEmitOutputFilePath(file, emitHost, '.js'); + }; + } + }(this._cmdLine); +} + +function _isDefaultEmpty(src: string): boolean { + return src + .replace('"use strict";', '') + .replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '$1') + .trim().length === 0; +} diff --git a/extensions/debug-auto-launch/src/extension.ts b/extensions/debug-auto-launch/src/extension.ts index 572c468d843..86de0ac6ea4 100644 --- a/extensions/debug-auto-launch/src/extension.ts +++ b/extensions/debug-auto-launch/src/extension.ts @@ -8,6 +8,12 @@ import { createServer, Server } from 'net'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; +const enum State { + Disabled = 'disabled', + OnlyWithFlag = 'onlyWithFlag', + Smart = 'smart', + Always = 'always', +} const localize = nls.loadMessageBundle(); const TEXT_STATUSBAR_LABEL = { [State.Disabled]: localize('status.text.auto.attach.disabled', 'Auto Attach: Disabled'), @@ -62,12 +68,6 @@ const SETTINGS_CAUSE_REFRESH = new Set( ['autoAttachSmartPattern', SETTING_STATE].map(s => `${SETTING_SECTION}.${s}`), ); -const enum State { - Disabled = 'disabled', - OnlyWithFlag = 'onlyWithFlag', - Smart = 'smart', - Always = 'always', -} let currentState: Promise<{ context: vscode.ExtensionContext; state: State | null }>; let statusItem: vscode.StatusBarItem | undefined; // and there is no status bar item From d132489cd09f5a5648a3fac75a13a9d3c8c47d82 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Wed, 15 Jun 2022 16:53:02 +0200 Subject: [PATCH 046/175] Bring the css loader plugin into our sources (#152205) --- .eslintignore | 2 - .eslintrc.json | 2 +- build/filters.js | 2 - build/gulpfile.editor.js | 3 - build/lib/standalone.js | 6 +- build/lib/standalone.ts | 6 +- src/tsconfig.monaco.json | 3 +- src/vs/css.build.js | 359 --------------------------------------- src/vs/css.build.ts | 290 +++++++++++++++++++++++++++++++ src/vs/css.d.ts | 5 - src/vs/css.js | 111 ------------ src/vs/css.ts | 93 ++++++++++ src/vs/loader.d.ts | 35 ++++ 13 files changed, 426 insertions(+), 491 deletions(-) delete mode 100644 src/vs/css.build.js create mode 100644 src/vs/css.build.ts delete mode 100644 src/vs/css.d.ts delete mode 100644 src/vs/css.js create mode 100644 src/vs/css.ts create mode 100644 src/vs/loader.d.ts diff --git a/.eslintignore b/.eslintignore index 1c49d5b8846..829fd016672 100644 --- a/.eslintignore +++ b/.eslintignore @@ -25,8 +25,6 @@ **/src/typings/**/*.d.ts **/src/vs/*/**/*.d.ts **/src/vs/base/test/common/filters.perf.data.js -**/src/vs/css.build.js -**/src/vs/css.js **/src/vs/loader.js **/src/vs/nls.build.js **/src/vs/nls.js diff --git a/.eslintrc.json b/.eslintrc.json index 9e9b1ddabfb..fa9e0d1ba89 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -577,7 +577,7 @@ "restrictions": [] }, { - "target": "src/vs/{css.d.ts,monaco.d.ts,nls.d.ts,nls.mock.ts}", + "target": "src/vs/{loader.d.ts,css.ts,css.build.ts,monaco.d.ts,nls.d.ts,nls.mock.ts}", "restrictions": [] }, { diff --git a/build/filters.js b/build/filters.js index c2a0a52bdb9..3d72c28e10b 100644 --- a/build/filters.js +++ b/build/filters.js @@ -68,8 +68,6 @@ module.exports.indentationFilter = [ '!**/LICENSE', '!src/vs/nls.js', '!src/vs/nls.build.js', - '!src/vs/css.js', - '!src/vs/css.build.js', '!src/vs/loader.js', '!src/vs/base/browser/dompurify/*', '!src/vs/base/common/marked/marked.js', diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 45546aa9e64..04b23c2ef17 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -113,9 +113,6 @@ const createESMSourcesAndResourcesTask = task.define('extract-editor-esm', () => 'vs/nls.ts', 'vs/nls.build.js', 'vs/nls.d.ts', - 'vs/css.js', - 'vs/css.build.js', - 'vs/css.d.ts', 'vs/base/worker/workerMain.ts', ], renames: { diff --git a/build/lib/standalone.js b/build/lib/standalone.js index 192a436899f..59d8fee343e 100644 --- a/build/lib/standalone.js +++ b/build/lib/standalone.js @@ -102,10 +102,10 @@ function extractEditor(options) { delete tsConfig.compilerOptions.moduleResolution; writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t')); [ - 'vs/css.build.js', - 'vs/css.d.ts', - 'vs/css.js', + 'vs/css.build.ts', + 'vs/css.ts', 'vs/loader.js', + 'vs/loader.d.ts', 'vs/nls.build.js', 'vs/nls.d.ts', 'vs/nls.js', diff --git a/build/lib/standalone.ts b/build/lib/standalone.ts index 992d1ab5288..34d2e2c9074 100644 --- a/build/lib/standalone.ts +++ b/build/lib/standalone.ts @@ -115,10 +115,10 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t')); [ - 'vs/css.build.js', - 'vs/css.d.ts', - 'vs/css.js', + 'vs/css.build.ts', + 'vs/css.ts', 'vs/loader.js', + 'vs/loader.d.ts', 'vs/nls.build.js', 'vs/nls.d.ts', 'vs/nls.js', diff --git a/src/tsconfig.monaco.json b/src/tsconfig.monaco.json index 7d18928f6b4..628e877dcd4 100644 --- a/src/tsconfig.monaco.json +++ b/src/tsconfig.monaco.json @@ -18,9 +18,8 @@ "include": [ "typings/require.d.ts", "typings/thenable.d.ts", - "vs/css.d.ts", + "vs/loader.d.ts", "vs/monaco.d.ts", - "vs/nls.d.ts", "vs/editor/*", "vs/base/common/*", "vs/base/browser/*", diff --git a/src/vs/css.build.js b/src/vs/css.build.js deleted file mode 100644 index c8875859684..00000000000 --- a/src/vs/css.build.js +++ /dev/null @@ -1,359 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -/*--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - * Please make sure to make edits in the .ts file at https://github.com/microsoft/vscode-loader/ - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------*/ -'use strict'; -var _cssPluginGlobal = this; -var CSSBuildLoaderPlugin; -(function (CSSBuildLoaderPlugin) { - var global = (_cssPluginGlobal || {}); - var BrowserCSSLoader = /** @class */ (function () { - function BrowserCSSLoader() { - this._pendingLoads = 0; - } - BrowserCSSLoader.prototype.attachListeners = function (name, linkNode, callback, errorback) { - var unbind = function () { - linkNode.removeEventListener('load', loadEventListener); - linkNode.removeEventListener('error', errorEventListener); - }; - var loadEventListener = function (e) { - unbind(); - callback(); - }; - var errorEventListener = function (e) { - unbind(); - errorback(e); - }; - linkNode.addEventListener('load', loadEventListener); - linkNode.addEventListener('error', errorEventListener); - }; - BrowserCSSLoader.prototype._onLoad = function (name, callback) { - this._pendingLoads--; - callback(); - }; - BrowserCSSLoader.prototype._onLoadError = function (name, errorback, err) { - this._pendingLoads--; - errorback(err); - }; - BrowserCSSLoader.prototype._insertLinkNode = function (linkNode) { - this._pendingLoads++; - var head = document.head || document.getElementsByTagName('head')[0]; - var other = head.getElementsByTagName('link') || head.getElementsByTagName('script'); - if (other.length > 0) { - head.insertBefore(linkNode, other[other.length - 1]); - } - else { - head.appendChild(linkNode); - } - }; - BrowserCSSLoader.prototype.createLinkTag = function (name, cssUrl, externalCallback, externalErrorback) { - var _this = this; - var linkNode = document.createElement('link'); - linkNode.setAttribute('rel', 'stylesheet'); - linkNode.setAttribute('type', 'text/css'); - linkNode.setAttribute('data-name', name); - var callback = function () { return _this._onLoad(name, externalCallback); }; - var errorback = function (err) { return _this._onLoadError(name, externalErrorback, err); }; - this.attachListeners(name, linkNode, callback, errorback); - linkNode.setAttribute('href', cssUrl); - return linkNode; - }; - BrowserCSSLoader.prototype._linkTagExists = function (name, cssUrl) { - var i, len, nameAttr, hrefAttr, links = document.getElementsByTagName('link'); - for (i = 0, len = links.length; i < len; i++) { - nameAttr = links[i].getAttribute('data-name'); - hrefAttr = links[i].getAttribute('href'); - if (nameAttr === name || hrefAttr === cssUrl) { - return true; - } - } - return false; - }; - BrowserCSSLoader.prototype.load = function (name, cssUrl, externalCallback, externalErrorback) { - if (this._linkTagExists(name, cssUrl)) { - externalCallback(); - return; - } - var linkNode = this.createLinkTag(name, cssUrl, externalCallback, externalErrorback); - this._insertLinkNode(linkNode); - }; - return BrowserCSSLoader; - }()); - var NodeCSSLoader = /** @class */ (function () { - function NodeCSSLoader() { - this.fs = require.nodeRequire('fs'); - } - NodeCSSLoader.prototype.load = function (name, cssUrl, externalCallback, externalErrorback) { - var contents = this.fs.readFileSync(cssUrl, 'utf8'); - // Remove BOM - if (contents.charCodeAt(0) === NodeCSSLoader.BOM_CHAR_CODE) { - contents = contents.substring(1); - } - externalCallback(contents); - }; - NodeCSSLoader.BOM_CHAR_CODE = 65279; - return NodeCSSLoader; - }()); - // ------------------------------ Finally, the plugin - var CSSPlugin = /** @class */ (function () { - function CSSPlugin(cssLoader) { - this.cssLoader = cssLoader; - } - CSSPlugin.prototype.load = function (name, req, load, config) { - config = config || {}; - var myConfig = config['vs/css'] || {}; - global.inlineResources = myConfig.inlineResources; - global.inlineResourcesLimit = myConfig.inlineResourcesLimit || 5000; - var cssUrl = req.toUrl(name + '.css'); - this.cssLoader.load(name, cssUrl, function (contents) { - // Contents has the CSS file contents if we are in a build - if (config.isBuild) { - CSSPlugin.BUILD_MAP[name] = contents; - CSSPlugin.BUILD_PATH_MAP[name] = cssUrl; - } - load({}); - }, function (err) { - if (typeof load.error === 'function') { - load.error('Could not find ' + cssUrl + ' or it was empty'); - } - }); - }; - CSSPlugin.prototype.write = function (pluginName, moduleName, write) { - // getEntryPoint is a Monaco extension to r.js - var entryPoint = write.getEntryPoint(); - // r.js destroys the context of this plugin between calling 'write' and 'writeFile' - // so the only option at this point is to leak the data to a global - global.cssPluginEntryPoints = global.cssPluginEntryPoints || {}; - global.cssPluginEntryPoints[entryPoint] = global.cssPluginEntryPoints[entryPoint] || []; - global.cssPluginEntryPoints[entryPoint].push({ - moduleName: moduleName, - contents: CSSPlugin.BUILD_MAP[moduleName], - fsPath: CSSPlugin.BUILD_PATH_MAP[moduleName], - }); - write.asModule(pluginName + '!' + moduleName, 'define([\'vs/css!' + entryPoint + '\'], {});'); - }; - CSSPlugin.prototype.writeFile = function (pluginName, moduleName, req, write, config) { - if (global.cssPluginEntryPoints && global.cssPluginEntryPoints.hasOwnProperty(moduleName)) { - var fileName = req.toUrl(moduleName + '.css'); - var contents = [ - '/*---------------------------------------------------------', - ' * Copyright (c) Microsoft Corporation. All rights reserved.', - ' *--------------------------------------------------------*/' - ], entries = global.cssPluginEntryPoints[moduleName]; - for (var i = 0; i < entries.length; i++) { - if (global.inlineResources) { - contents.push(Utilities.rewriteOrInlineUrls(entries[i].fsPath, entries[i].moduleName, moduleName, entries[i].contents, global.inlineResources === 'base64', global.inlineResourcesLimit)); - } - else { - contents.push(Utilities.rewriteUrls(entries[i].moduleName, moduleName, entries[i].contents)); - } - } - write(fileName, contents.join('\r\n')); - } - }; - CSSPlugin.prototype.getInlinedResources = function () { - return global.cssInlinedResources || []; - }; - CSSPlugin.BUILD_MAP = {}; - CSSPlugin.BUILD_PATH_MAP = {}; - return CSSPlugin; - }()); - CSSBuildLoaderPlugin.CSSPlugin = CSSPlugin; - var Utilities = /** @class */ (function () { - function Utilities() { - } - Utilities.startsWith = function (haystack, needle) { - return haystack.length >= needle.length && haystack.substr(0, needle.length) === needle; - }; - /** - * Find the path of a file. - */ - Utilities.pathOf = function (filename) { - var lastSlash = filename.lastIndexOf('/'); - if (lastSlash !== -1) { - return filename.substr(0, lastSlash + 1); - } - else { - return ''; - } - }; - /** - * A conceptual a + b for paths. - * Takes into account if `a` contains a protocol. - * Also normalizes the result: e.g.: a/b/ + ../c => a/c - */ - Utilities.joinPaths = function (a, b) { - function findSlashIndexAfterPrefix(haystack, prefix) { - if (Utilities.startsWith(haystack, prefix)) { - return Math.max(prefix.length, haystack.indexOf('/', prefix.length)); - } - return 0; - } - var aPathStartIndex = 0; - aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, '//'); - aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'http://'); - aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'https://'); - function pushPiece(pieces, piece) { - if (piece === './') { - // Ignore - return; - } - if (piece === '../') { - var prevPiece = (pieces.length > 0 ? pieces[pieces.length - 1] : null); - if (prevPiece && prevPiece === '/') { - // Ignore - return; - } - if (prevPiece && prevPiece !== '../') { - // Pop - pieces.pop(); - return; - } - } - // Push - pieces.push(piece); - } - function push(pieces, path) { - while (path.length > 0) { - var slashIndex = path.indexOf('/'); - var piece = (slashIndex >= 0 ? path.substring(0, slashIndex + 1) : path); - path = (slashIndex >= 0 ? path.substring(slashIndex + 1) : ''); - pushPiece(pieces, piece); - } - } - var pieces = []; - push(pieces, a.substr(aPathStartIndex)); - if (b.length > 0 && b.charAt(0) === '/') { - pieces = []; - } - push(pieces, b); - return a.substring(0, aPathStartIndex) + pieces.join(''); - }; - Utilities.commonPrefix = function (str1, str2) { - var len = Math.min(str1.length, str2.length); - for (var i = 0; i < len; i++) { - if (str1.charCodeAt(i) !== str2.charCodeAt(i)) { - break; - } - } - return str1.substring(0, i); - }; - Utilities.commonFolderPrefix = function (fromPath, toPath) { - var prefix = Utilities.commonPrefix(fromPath, toPath); - var slashIndex = prefix.lastIndexOf('/'); - if (slashIndex === -1) { - return ''; - } - return prefix.substring(0, slashIndex + 1); - }; - Utilities.relativePath = function (fromPath, toPath) { - if (Utilities.startsWith(toPath, '/') || Utilities.startsWith(toPath, 'http://') || Utilities.startsWith(toPath, 'https://')) { - return toPath; - } - // Ignore common folder prefix - var prefix = Utilities.commonFolderPrefix(fromPath, toPath); - fromPath = fromPath.substr(prefix.length); - toPath = toPath.substr(prefix.length); - var upCount = fromPath.split('/').length; - var result = ''; - for (var i = 1; i < upCount; i++) { - result += '../'; - } - return result + toPath; - }; - Utilities._replaceURL = function (contents, replacer) { - // Use ")" as the terminator as quotes are oftentimes not used at all - return contents.replace(/url\(\s*([^\)]+)\s*\)?/g, function (_) { - var matches = []; - for (var _i = 1; _i < arguments.length; _i++) { - matches[_i - 1] = arguments[_i]; - } - var url = matches[0]; - // Eliminate starting quotes (the initial whitespace is not captured) - if (url.charAt(0) === '"' || url.charAt(0) === '\'') { - url = url.substring(1); - } - // The ending whitespace is captured - while (url.length > 0 && (url.charAt(url.length - 1) === ' ' || url.charAt(url.length - 1) === '\t')) { - url = url.substring(0, url.length - 1); - } - // Eliminate ending quotes - if (url.charAt(url.length - 1) === '"' || url.charAt(url.length - 1) === '\'') { - url = url.substring(0, url.length - 1); - } - if (!Utilities.startsWith(url, 'data:') && !Utilities.startsWith(url, 'http://') && !Utilities.startsWith(url, 'https://')) { - url = replacer(url); - } - return 'url(' + url + ')'; - }); - }; - Utilities.rewriteUrls = function (originalFile, newFile, contents) { - return this._replaceURL(contents, function (url) { - var absoluteUrl = Utilities.joinPaths(Utilities.pathOf(originalFile), url); - return Utilities.relativePath(newFile, absoluteUrl); - }); - }; - Utilities.rewriteOrInlineUrls = function (originalFileFSPath, originalFile, newFile, contents, forceBase64, inlineByteLimit) { - var fs = require.nodeRequire('fs'); - var path = require.nodeRequire('path'); - return this._replaceURL(contents, function (url) { - if (/\.(svg|png)$/.test(url)) { - var fsPath = path.join(path.dirname(originalFileFSPath), url); - var fileContents = fs.readFileSync(fsPath); - if (fileContents.length < inlineByteLimit) { - global.cssInlinedResources = global.cssInlinedResources || []; - var normalizedFSPath = fsPath.replace(/\\/g, '/'); - if (global.cssInlinedResources.indexOf(normalizedFSPath) >= 0) { - // console.warn('CSS INLINING IMAGE AT ' + fsPath + ' MORE THAN ONCE. CONSIDER CONSOLIDATING CSS RULES'); - } - global.cssInlinedResources.push(normalizedFSPath); - var MIME = /\.svg$/.test(url) ? 'image/svg+xml' : 'image/png'; - var DATA = ';base64,' + fileContents.toString('base64'); - if (!forceBase64 && /\.svg$/.test(url)) { - // .svg => url encode as explained at https://codepen.io/tigt/post/optimizing-svgs-in-data-uris - var newText = fileContents.toString() - .replace(/"/g, '\'') - .replace(/%/g, '%25') - .replace(//g, '%3E') - .replace(/&/g, '%26') - .replace(/#/g, '%23') - .replace(/\s+/g, ' '); - var encodedData = ',' + newText; - if (encodedData.length < DATA.length) { - DATA = encodedData; - } - } - return '"data:' + MIME + DATA + '"'; - } - } - var absoluteUrl = Utilities.joinPaths(Utilities.pathOf(originalFile), url); - return Utilities.relativePath(newFile, absoluteUrl); - }); - }; - return Utilities; - }()); - CSSBuildLoaderPlugin.Utilities = Utilities; - (function () { - var cssLoader = null; - var isElectron = (typeof process !== 'undefined' && typeof process.versions !== 'undefined' && typeof process.versions['electron'] !== 'undefined'); - if (typeof process !== 'undefined' && process.versions && !!process.versions.node && !isElectron) { - cssLoader = new NodeCSSLoader(); - } - else { - cssLoader = new BrowserCSSLoader(); - } - define('vs/css', new CSSPlugin(cssLoader)); - })(); -})(CSSBuildLoaderPlugin || (CSSBuildLoaderPlugin = {})); diff --git a/src/vs/css.build.ts b/src/vs/css.build.ts new file mode 100644 index 00000000000..2107d144622 --- /dev/null +++ b/src/vs/css.build.ts @@ -0,0 +1,290 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +namespace CSSBuildLoaderPlugin { + + interface ICSSPluginConfig { + inlineResources?: boolean | 'base64'; + inlineResourcesLimit?: number; + } + + // This file gets compiled also with the standalone editor, + // so we cannot depend on types from node.d.ts + interface INodeFS { + readFileSync(path: string, encoding: 'utf8'): string; + readFileSync(path: string): INodeBuffer; + } + interface INodeBuffer { + length: number; + toString(encoding?: 'base64'): string; + } + interface INodePath { + dirname(p: string): string; + join(...paths: string[]): string; + } + + const fs: INodeFS = (require).nodeRequire('fs'); + const path: INodePath = (require).nodeRequire('path'); + + interface ICSSEntryPointData { + moduleName: string; + contents: string; + fsPath: string; + } + + export class CSSPlugin implements AMDLoader.ILoaderPlugin { + + private inlineResources: boolean | 'base64' = false; + private inlineResourcesLimit: number = 5000; + + private readonly _contentsMap: { [moduleName: string]: string } = {}; + private readonly _pathMap: { [moduleName: string]: string } = {}; + private readonly _entryPoints: { [entryPoint: string]: ICSSEntryPointData[] } = {}; + private readonly _inlinedResources: string[] = []; + + public load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void { + config = config || {}; + const myConfig = (config['vs/css'] || {}); + this.inlineResources = (typeof myConfig.inlineResources === 'undefined' ? false : myConfig.inlineResources); + this.inlineResourcesLimit = (myConfig.inlineResourcesLimit || 5000); + const cssUrl = req.toUrl(name + '.css'); + let contents = fs.readFileSync(cssUrl, 'utf8'); + if (contents.charCodeAt(0) === 65279 /* BOM */) { + // Remove BOM + contents = contents.substring(1); + } + if (config.isBuild) { + this._contentsMap[name] = contents; + this._pathMap[name] = cssUrl; + } + load({}); + } + + public write(pluginName: string, moduleName: string, write: AMDLoader.IPluginWriteCallback): void { + const entryPoint = write.getEntryPoint(); + + this._entryPoints[entryPoint] = this._entryPoints[entryPoint] || []; + this._entryPoints[entryPoint].push({ + moduleName: moduleName, + contents: this._contentsMap[moduleName], + fsPath: this._pathMap[moduleName], + }); + + write.asModule(pluginName + '!' + moduleName, + 'define([\'vs/css!' + entryPoint + '\'], {});' + ); + } + + public writeFile(pluginName: string, moduleName: string, req: AMDLoader.IRelativeRequire, write: AMDLoader.IPluginWriteFileCallback, config: AMDLoader.IConfigurationOptions): void { + if (this._entryPoints && this._entryPoints.hasOwnProperty(moduleName)) { + const fileName = req.toUrl(moduleName + '.css'); + const contents = [ + '/*---------------------------------------------------------', + ' * Copyright (c) Microsoft Corporation. All rights reserved.', + ' *--------------------------------------------------------*/' + ], + entries = this._entryPoints[moduleName]; + for (let i = 0; i < entries.length; i++) { + if (this.inlineResources) { + contents.push(this._rewriteOrInlineUrls(entries[i].fsPath, entries[i].moduleName, moduleName, entries[i].contents, this.inlineResources === 'base64', this.inlineResourcesLimit)); + } else { + contents.push(this._rewriteUrls(entries[i].moduleName, moduleName, entries[i].contents)); + } + } + write(fileName, contents.join('\r\n')); + } + } + + public getInlinedResources(): string[] { + return this._inlinedResources || []; + } + + private _rewriteOrInlineUrls(originalFileFSPath: string, originalFile: string, newFile: string, contents: string, forceBase64: boolean, inlineByteLimit: number): string { + return Utilities.replaceURL(contents, (url) => { + if (/\.(svg|png)$/.test(url)) { + const fsPath = path.join(path.dirname(originalFileFSPath), url); + const fileContents = fs.readFileSync(fsPath); + + if (fileContents.length < inlineByteLimit) { + const normalizedFSPath = fsPath.replace(/\\/g, '/'); + this._inlinedResources.push(normalizedFSPath); + + const MIME = /\.svg$/.test(url) ? 'image/svg+xml' : 'image/png'; + let DATA = ';base64,' + fileContents.toString('base64'); + + if (!forceBase64 && /\.svg$/.test(url)) { + // .svg => url encode as explained at https://codepen.io/tigt/post/optimizing-svgs-in-data-uris + const newText = fileContents.toString() + .replace(/"/g, '\'') + .replace(/%/g, '%25') + .replace(//g, '%3E') + .replace(/&/g, '%26') + .replace(/#/g, '%23') + .replace(/\s+/g, ' '); + const encodedData = ',' + newText; + if (encodedData.length < DATA.length) { + DATA = encodedData; + } + } + return '"data:' + MIME + DATA + '"'; + } + } + + const absoluteUrl = Utilities.joinPaths(Utilities.pathOf(originalFile), url); + return Utilities.relativePath(newFile, absoluteUrl); + }); + } + + private _rewriteUrls(originalFile: string, newFile: string, contents: string): string { + return Utilities.replaceURL(contents, (url) => { + const absoluteUrl = Utilities.joinPaths(Utilities.pathOf(originalFile), url); + return Utilities.relativePath(newFile, absoluteUrl); + }); + } + } + + export class Utilities { + + public static startsWith(haystack: string, needle: string): boolean { + return haystack.length >= needle.length && haystack.substr(0, needle.length) === needle; + } + + /** + * Find the path of a file. + */ + public static pathOf(filename: string): string { + const lastSlash = filename.lastIndexOf('/'); + if (lastSlash !== -1) { + return filename.substr(0, lastSlash + 1); + } else { + return ''; + } + } + + /** + * A conceptual a + b for paths. + * Takes into account if `a` contains a protocol. + * Also normalizes the result: e.g.: a/b/ + ../c => a/c + */ + public static joinPaths(a: string, b: string): string { + + function findSlashIndexAfterPrefix(haystack: string, prefix: string): number { + if (Utilities.startsWith(haystack, prefix)) { + return Math.max(prefix.length, haystack.indexOf('/', prefix.length)); + } + return 0; + } + + let aPathStartIndex = 0; + aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, '//'); + aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'http://'); + aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'https://'); + + function pushPiece(pieces: string[], piece: string): void { + if (piece === './') { + // Ignore + return; + } + if (piece === '../') { + const prevPiece = (pieces.length > 0 ? pieces[pieces.length - 1] : null); + if (prevPiece && prevPiece === '/') { + // Ignore + return; + } + if (prevPiece && prevPiece !== '../') { + // Pop + pieces.pop(); + return; + } + } + // Push + pieces.push(piece); + } + + function push(pieces: string[], path: string): void { + while (path.length > 0) { + const slashIndex = path.indexOf('/'); + const piece = (slashIndex >= 0 ? path.substring(0, slashIndex + 1) : path); + path = (slashIndex >= 0 ? path.substring(slashIndex + 1) : ''); + pushPiece(pieces, piece); + } + } + + let pieces: string[] = []; + push(pieces, a.substr(aPathStartIndex)); + if (b.length > 0 && b.charAt(0) === '/') { + pieces = []; + } + push(pieces, b); + + return a.substring(0, aPathStartIndex) + pieces.join(''); + } + + public static commonPrefix(str1: string, str2: string): string { + const len = Math.min(str1.length, str2.length); + for (let i = 0; i < len; i++) { + if (str1.charCodeAt(i) !== str2.charCodeAt(i)) { + return str1.substring(0, i); + } + } + return str1.substring(0, len); + } + + public static commonFolderPrefix(fromPath: string, toPath: string): string { + const prefix = Utilities.commonPrefix(fromPath, toPath); + const slashIndex = prefix.lastIndexOf('/'); + if (slashIndex === -1) { + return ''; + } + return prefix.substring(0, slashIndex + 1); + } + + public static relativePath(fromPath: string, toPath: string): string { + if (Utilities.startsWith(toPath, '/') || Utilities.startsWith(toPath, 'http://') || Utilities.startsWith(toPath, 'https://')) { + return toPath; + } + + // Ignore common folder prefix + const prefix = Utilities.commonFolderPrefix(fromPath, toPath); + fromPath = fromPath.substr(prefix.length); + toPath = toPath.substr(prefix.length); + + const upCount = fromPath.split('/').length; + let result = ''; + for (let i = 1; i < upCount; i++) { + result += '../'; + } + return result + toPath; + } + + public static replaceURL(contents: string, replacer: (url: string) => string): string { + // Use ")" as the terminator as quotes are oftentimes not used at all + return contents.replace(/url\(\s*([^\)]+)\s*\)?/g, (_: string, ...matches: string[]) => { + let url = matches[0]; + // Eliminate starting quotes (the initial whitespace is not captured) + if (url.charAt(0) === '"' || url.charAt(0) === '\'') { + url = url.substring(1); + } + // The ending whitespace is captured + while (url.length > 0 && (url.charAt(url.length - 1) === ' ' || url.charAt(url.length - 1) === '\t')) { + url = url.substring(0, url.length - 1); + } + // Eliminate ending quotes + if (url.charAt(url.length - 1) === '"' || url.charAt(url.length - 1) === '\'') { + url = url.substring(0, url.length - 1); + } + + if (!Utilities.startsWith(url, 'data:') && !Utilities.startsWith(url, 'http://') && !Utilities.startsWith(url, 'https://')) { + url = replacer(url); + } + + return 'url(' + url + ')'; + }); + } + } + + define('vs/css', new CSSPlugin()); +} diff --git a/src/vs/css.d.ts b/src/vs/css.d.ts deleted file mode 100644 index 11a10ed5950..00000000000 --- a/src/vs/css.d.ts +++ /dev/null @@ -1,5 +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/src/vs/css.js b/src/vs/css.js deleted file mode 100644 index 8a0f9912902..00000000000 --- a/src/vs/css.js +++ /dev/null @@ -1,111 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -/*--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - * Please make sure to make edits in the .ts file at https://github.com/microsoft/vscode-loader/ - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------*/ -'use strict'; -var CSSLoaderPlugin; -(function (CSSLoaderPlugin) { - var BrowserCSSLoader = /** @class */ (function () { - function BrowserCSSLoader() { - this._pendingLoads = 0; - } - BrowserCSSLoader.prototype.attachListeners = function (name, linkNode, callback, errorback) { - var unbind = function () { - linkNode.removeEventListener('load', loadEventListener); - linkNode.removeEventListener('error', errorEventListener); - }; - var loadEventListener = function (e) { - unbind(); - callback(); - }; - var errorEventListener = function (e) { - unbind(); - errorback(e); - }; - linkNode.addEventListener('load', loadEventListener); - linkNode.addEventListener('error', errorEventListener); - }; - BrowserCSSLoader.prototype._onLoad = function (name, callback) { - this._pendingLoads--; - callback(); - }; - BrowserCSSLoader.prototype._onLoadError = function (name, errorback, err) { - this._pendingLoads--; - errorback(err); - }; - BrowserCSSLoader.prototype._insertLinkNode = function (linkNode) { - this._pendingLoads++; - var head = document.head || document.getElementsByTagName('head')[0]; - head.appendChild(linkNode); - }; - BrowserCSSLoader.prototype.createLinkTag = function (name, cssUrl, externalCallback, externalErrorback) { - var _this = this; - var linkNode = document.createElement('link'); - linkNode.setAttribute('rel', 'stylesheet'); - linkNode.setAttribute('type', 'text/css'); - linkNode.setAttribute('data-name', name); - var callback = function () { return _this._onLoad(name, externalCallback); }; - var errorback = function (err) { return _this._onLoadError(name, externalErrorback, err); }; - this.attachListeners(name, linkNode, callback, errorback); - linkNode.setAttribute('href', cssUrl); - return linkNode; - }; - BrowserCSSLoader.prototype._linkTagExists = function (name, cssUrl) { - var i, len, nameAttr, hrefAttr, links = document.getElementsByTagName('link'); - for (i = 0, len = links.length; i < len; i++) { - nameAttr = links[i].getAttribute('data-name'); - hrefAttr = links[i].getAttribute('href'); - if (nameAttr === name || hrefAttr === cssUrl) { - return true; - } - } - return false; - }; - BrowserCSSLoader.prototype.load = function (name, cssUrl, externalCallback, externalErrorback) { - if (this._linkTagExists(name, cssUrl)) { - externalCallback(); - return; - } - var linkNode = this.createLinkTag(name, cssUrl, externalCallback, externalErrorback); - this._insertLinkNode(linkNode); - }; - return BrowserCSSLoader; - }()); - // ------------------------------ Finally, the plugin - var CSSPlugin = /** @class */ (function () { - function CSSPlugin() { - this._cssLoader = new BrowserCSSLoader(); - } - CSSPlugin.prototype.load = function (name, req, load, config) { - config = config || {}; - var cssConfig = config['vs/css'] || {}; - if (cssConfig.disabled) { - // the plugin is asked to not create any style sheets - load({}); - return; - } - var cssUrl = req.toUrl(name + '.css'); - this._cssLoader.load(name, cssUrl, function (contents) { - load({}); - }, function (err) { - if (typeof load.error === 'function') { - load.error('Could not find ' + cssUrl + ' or it was empty'); - } - }); - }; - return CSSPlugin; - }()); - CSSLoaderPlugin.CSSPlugin = CSSPlugin; - define('vs/css', new CSSPlugin()); -})(CSSLoaderPlugin || (CSSLoaderPlugin = {})); diff --git a/src/vs/css.ts b/src/vs/css.ts new file mode 100644 index 00000000000..942040b4af5 --- /dev/null +++ b/src/vs/css.ts @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +namespace CSSLoaderPlugin { + + interface ICSSPluginConfig { + disabled?: boolean; + } + + class BrowserCSSLoader { + + public load(name: string, cssUrl: string, callback: () => void, errorback: (err: any) => void): void { + if (this._linkTagExists(name, cssUrl)) { + callback(); + return; + } + this._createLinkTag(name, cssUrl, callback, errorback); + } + + private _linkTagExists(name: string, cssUrl: string): boolean { + const links = document.getElementsByTagName('link'); + for (let i = 0, len = links.length; i < len; i++) { + const nameAttr = links[i].getAttribute('data-name'); + const hrefAttr = links[i].getAttribute('href'); + if (nameAttr === name || hrefAttr === cssUrl) { + return true; + } + } + return false; + } + + private _createLinkTag(name: string, cssUrl: string, callback: () => void, errorback: (err: any) => void): void { + const linkNode = document.createElement('link'); + linkNode.setAttribute('rel', 'stylesheet'); + linkNode.setAttribute('type', 'text/css'); + linkNode.setAttribute('data-name', name); + + this._attachListeners(name, linkNode, callback, errorback); + linkNode.setAttribute('href', cssUrl); + + const head = document.head || document.getElementsByTagName('head')[0]; + head.appendChild(linkNode); + } + + private _attachListeners(name: string, linkNode: HTMLLinkElement, callback: () => void, errorback: (err: any) => void): void { + const unbind = () => { + linkNode.removeEventListener('load', loadEventListener); + linkNode.removeEventListener('error', errorEventListener); + }; + const loadEventListener = (e: any) => { + unbind(); + callback(); + }; + const errorEventListener = (e: any) => { + unbind(); + errorback(e); + }; + linkNode.addEventListener('load', loadEventListener); + linkNode.addEventListener('error', errorEventListener); + } + } + + export class CSSPlugin implements AMDLoader.ILoaderPlugin { + + private _cssLoader = new BrowserCSSLoader(); + + public load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void { + config = config || {}; + const cssConfig = (config['vs/css'] || {}); + + if (cssConfig.disabled) { + // the plugin is asked to not create any style sheets + load({}); + return; + } + + const cssUrl = req.toUrl(name + '.css'); + this._cssLoader.load(name, cssUrl, () => { + load({}); + }, (err: any) => { + if (typeof load.error === 'function') { + load.error('Could not find ' + cssUrl + '.'); + } + }); + } + } + + define('vs/css', new CSSPlugin()); +} diff --git a/src/vs/loader.d.ts b/src/vs/loader.d.ts new file mode 100644 index 00000000000..5495321da78 --- /dev/null +++ b/src/vs/loader.d.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. + *--------------------------------------------------------------------------------------------*/ + +declare namespace AMDLoader { + interface ILoaderPlugin { + load: (pluginParam: string, parentRequire: IRelativeRequire, loadCallback: IPluginLoadCallback, options: IConfigurationOptions) => void; + write?: (pluginName: string, moduleName: string, write: IPluginWriteCallback) => void; + writeFile?: (pluginName: string, moduleName: string, req: IRelativeRequire, write: IPluginWriteFileCallback, config: IConfigurationOptions) => void; + finishBuild?: (write: (filename: string, contents: string) => void) => void; + } + interface IRelativeRequire { + (dependencies: string[], callback: Function, errorback?: (error: Error) => void): void; + toUrl(id: string): string; + } + interface IPluginLoadCallback { + (value: any): void; + error(err: any): void; + } + interface IConfigurationOptions { + isBuild: boolean | undefined; + [key: string]: any; + } + interface IPluginWriteCallback { + (contents: string): void; + getEntryPoint(): string; + asModule(moduleId: string, contents: string): void; + } + interface IPluginWriteFileCallback { + (filename: string, contents: string): void; + getEntryPoint(): string; + asModule(moduleId: string, contents: string): void; + } +} From 354e1a05958e51c25bab1df108c722c8105ebaca Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 15 Jun 2022 07:56:02 -0700 Subject: [PATCH 047/175] Finalize data file reading API (#152127) Fixes #147481 Also reverts #150963 since the `kind` field is not being finalized --- .../markdown-language-features/tsconfig.json | 1 - .../api/browser/mainThreadTreeViews.ts | 5 +- .../workbench/api/common/extHost.api.impl.ts | 1 - .../workbench/api/common/extHost.protocol.ts | 2 +- .../workbench/api/common/extHostTreeViews.ts | 5 +- .../api/common/extHostTypeConverters.ts | 2 - src/vs/workbench/api/common/extHostTypes.ts | 7 -- .../test/browser/mainThreadTreeViews.test.ts | 2 +- .../workbench/browser/parts/views/treeView.ts | 4 +- src/vs/workbench/common/views.ts | 1 - .../common/extensionsApiProposals.ts | 1 - src/vscode-dts/vscode.d.ts | 31 ++++++++ .../vscode.proposed.dataTransferFiles.d.ts | 71 ------------------- 13 files changed, 38 insertions(+), 95 deletions(-) delete mode 100644 src/vscode-dts/vscode.proposed.dataTransferFiles.d.ts diff --git a/extensions/markdown-language-features/tsconfig.json b/extensions/markdown-language-features/tsconfig.json index 7c1d4a7fca8..5b2636221ff 100644 --- a/extensions/markdown-language-features/tsconfig.json +++ b/extensions/markdown-language-features/tsconfig.json @@ -7,7 +7,6 @@ "src/**/*", "../../src/vscode-dts/vscode.d.ts", "../../src/vscode-dts/vscode.proposed.textEditorDrop.d.ts", - "../../src/vscode-dts/vscode.proposed.dataTransferFiles.d.ts", "../../src/vscode-dts/vscode.proposed.documentPaste.d.ts" ] } diff --git a/src/vs/workbench/api/browser/mainThreadTreeViews.ts b/src/vs/workbench/api/browser/mainThreadTreeViews.ts index 7c8aa9d2323..4753211a74b 100644 --- a/src/vs/workbench/api/browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/browser/mainThreadTreeViews.ts @@ -37,14 +37,14 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTreeViews); } - async $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean; canSelectMany: boolean; dropMimeTypes: string[]; dragMimeTypes: string[]; hasHandleDrag: boolean; hasHandleDrop: boolean; supportsFileDataTransfers: boolean }): Promise { + async $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean; canSelectMany: boolean; dropMimeTypes: string[]; dragMimeTypes: string[]; hasHandleDrag: boolean; hasHandleDrop: boolean }): Promise { this.logService.trace('MainThreadTreeViews#$registerTreeViewDataProvider', treeViewId, options); this.extensionService.whenInstalledExtensionsRegistered().then(() => { const dataProvider = new TreeViewDataProvider(treeViewId, this._proxy, this.notificationService); this._dataProviders.set(treeViewId, dataProvider); const dndController = (options.hasHandleDrag || options.hasHandleDrop) - ? new TreeViewDragAndDropController(treeViewId, options.dropMimeTypes, options.dragMimeTypes, options.hasHandleDrag, options.supportsFileDataTransfers, this._proxy) : undefined; + ? new TreeViewDragAndDropController(treeViewId, options.dropMimeTypes, options.dragMimeTypes, options.hasHandleDrag, this._proxy) : undefined; const viewer = this.getTreeView(treeViewId); if (viewer) { // Order is important here. The internal tree isn't created until the dataProvider is set. @@ -201,7 +201,6 @@ class TreeViewDragAndDropController implements ITreeViewDragAndDropController { readonly dropMimeTypes: string[], readonly dragMimeTypes: string[], readonly hasWillDrop: boolean, - readonly supportsFileDataTransfers: boolean, private readonly _proxy: ExtHostTreeViewsShape) { } async handleDrop(dataTransfer: VSDataTransfer, targetTreeItem: ITreeItem | undefined, token: CancellationToken, diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 4eb48fda17b..cca4ac70549 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1327,7 +1327,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I TextSearchCompleteMessageType: TextSearchCompleteMessageType, DataTransfer: extHostTypes.DataTransfer, DataTransferItem: extHostTypes.DataTransferItem, - DataTransferItemKind: extHostTypes.DataTransferItemKind, CoveredCount: extHostTypes.CoveredCount, FileCoverage: extHostTypes.FileCoverage, StatementCoverage: extHostTypes.StatementCoverage, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 73da98403c4..d38e5c3bc52 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -258,7 +258,7 @@ export interface MainThreadTextEditorsShape extends IDisposable { } export interface MainThreadTreeViewsShape extends IDisposable { - $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean; canSelectMany: boolean; dropMimeTypes: readonly string[]; dragMimeTypes: readonly string[]; hasHandleDrag: boolean; hasHandleDrop: boolean; supportsFileDataTransfers: boolean }): Promise; + $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean; canSelectMany: boolean; dropMimeTypes: readonly string[]; dragMimeTypes: readonly string[]; hasHandleDrag: boolean; hasHandleDrop: boolean }): Promise; $refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): Promise; $reveal(treeViewId: string, itemInfo: { item: ITreeItem; parentChain: ITreeItem[] } | undefined, options: IRevealOptions): Promise; $setMessage(treeViewId: string, message: string): void; diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index 8bdd508109c..fdbb535e422 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -24,7 +24,7 @@ import { IMarkdownString } from 'vs/base/common/htmlContent'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { Command } from 'vs/editor/common/languages'; import { ITreeViewsService, TreeviewsService } from 'vs/workbench/services/views/common/treeViewsService'; -import { checkProposedApiEnabled, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; +import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; type TreeItemHandle = string; @@ -92,8 +92,7 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { const dragMimeTypes = options.dragAndDropController?.dragMimeTypes ?? []; const hasHandleDrag = !!options.dragAndDropController?.handleDrag; const hasHandleDrop = !!options.dragAndDropController?.handleDrop; - const supportsFileDataTransfers = isProposedApiEnabled(extension, 'dataTransferFiles'); - const registerPromise = this._proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany, dropMimeTypes, dragMimeTypes, hasHandleDrag, hasHandleDrop, supportsFileDataTransfers }); + const registerPromise = this._proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany, dropMimeTypes, dragMimeTypes, hasHandleDrag, hasHandleDrop }); const treeView = this.createExtHostTreeView(viewId, options, extension); return { get onDidCollapseElement() { return treeView.onDidCollapseElement; }, diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index f37cc63a6f8..956ba5dc9c5 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1953,8 +1953,6 @@ export namespace DataTransferItem { const file = item.fileData; if (file) { return new class extends types.DataTransferItem { - override get kind() { return types.DataTransferItemKind.File; } - override asFile(): vscode.DataTransferFile { return { name: file.name, diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index d3bb21fb997..744177bb76c 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2428,16 +2428,9 @@ export enum TreeItemCollapsibleState { Expanded = 2 } -export enum DataTransferItemKind { - String = 1, - File = 2, -} - @es5ClassCompat export class DataTransferItem { - get kind(): DataTransferItemKind { return DataTransferItemKind.String; } - async asString(): Promise { return typeof this.value === 'string' ? this.value : JSON.stringify(this.value); } diff --git a/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts b/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts index 4afba6e05e6..1386a01b234 100644 --- a/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts @@ -75,7 +75,7 @@ suite('MainThreadHostTreeView', function () { } drain(): any { return null; } }, new TestViewsService(), new TestNotificationService(), testExtensionService, new NullLogService()); - mainThreadTreeViews.$registerTreeViewDataProvider(testTreeViewId, { showCollapseAll: false, canSelectMany: false, dropMimeTypes: [], dragMimeTypes: [], hasHandleDrag: false, hasHandleDrop: false, supportsFileDataTransfers: false }); + mainThreadTreeViews.$registerTreeViewDataProvider(testTreeViewId, { showCollapseAll: false, canSelectMany: false, dropMimeTypes: [], dragMimeTypes: [], hasHandleDrag: false, hasHandleDrop: false }); await testExtensionService.whenInstalledExtensionsRegistered(); }); diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 3250245d937..3d4dcc3eab6 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -1528,9 +1528,7 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { const file = dataItem.getAsFile(); if (file) { uris.push(URI.file(file.path)); - if (dndController.supportsFileDataTransfers) { - treeDataTransfer.append(type, createFileDataTransferItemFromFile(file)); - } + treeDataTransfer.append(type, createFileDataTransferItemFromFile(file)); } } } diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index a255a45c0e2..ac9b39c0c21 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -831,7 +831,6 @@ export interface ITreeViewDataProvider { export interface ITreeViewDragAndDropController { readonly dropMimeTypes: string[]; readonly dragMimeTypes: string[]; - readonly supportsFileDataTransfers: boolean; handleDrag(sourceTreeItemHandles: string[], operationUuid: string, token: CancellationToken): Promise; handleDrop(elements: VSDataTransfer, target: ITreeItem | undefined, token: CancellationToken, operationUuid?: string, sourceTreeId?: string, sourceTreeItemHandles?: string[]): Promise; } diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 080d4b9f534..a99692ce7cb 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -16,7 +16,6 @@ export const allApiProposals = Object.freeze({ contribViewsRemote: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribViewsRemote.d.ts', contribViewsWelcome: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribViewsWelcome.d.ts', customEditorMove: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.customEditorMove.d.ts', - dataTransferFiles: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.dataTransferFiles.d.ts', diffCommand: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.diffCommand.d.ts', documentFiltersExclusive: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.documentFiltersExclusive.d.ts', documentPaste: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.documentPaste.d.ts', diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index d701f46f418..c94a4461885 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -10054,7 +10054,28 @@ declare module 'vscode' { * `true` if the {@link TreeView tree view} is visible otherwise `false`. */ readonly visible: boolean; + } + /** + * A file associated with a {@linkcode DataTransferItem}. + */ + export interface DataTransferFile { + /** + * The name of the file. + */ + readonly name: string; + + /** + * The full file path of the file. + * + * May be `undefined` on web. + */ + readonly uri?: Uri; + + /** + * The full file contents of the file. + */ + data(): Thenable; } /** @@ -10068,6 +10089,16 @@ declare module 'vscode' { */ asString(): Thenable; + /** + * Try getting the {@link DataTransferFile file} associated with this data transfer item. + * + * Note that the file object is only valid for the scope of the drag and drop operation. + * + * @returns The file for the data transfer or `undefined` if the item is either not a file or the + * file data cannot be accessed. + */ + asFile(): DataTransferFile | undefined; + /** * Custom data stored on this item. * diff --git a/src/vscode-dts/vscode.proposed.dataTransferFiles.d.ts b/src/vscode-dts/vscode.proposed.dataTransferFiles.d.ts deleted file mode 100644 index 68df3c07f4a..00000000000 --- a/src/vscode-dts/vscode.proposed.dataTransferFiles.d.ts +++ /dev/null @@ -1,71 +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 module 'vscode' { - - // https://github.com/microsoft/vscode/issues/147481 - - /** - * A file associated with a {@linkcode DataTransferItem}. - */ - interface DataTransferFile { - /** - * The name of the file. - */ - readonly name: string; - - /** - * The full file path of the file. - * - * May be undefined on web. - */ - readonly uri?: Uri; - - /** - * The full file contents of the file. - */ - data(): Thenable; - } - - /** - * Identifies the kind of a {@link DataTransferItem}. May either be {@linkcode DataTransferItemKind.String String} or {@linkcode DataTransferItemKind.File File}. - */ - enum DataTransferItemKind { - - /** - * The {@link DataTransferItem} is a string. - * - * Use {@link DataTransferItem.asString} to get a string representation of this item or - * {@link DataTransferItem.value} to access the original value if them item was created - * by an extension. - */ - String = 1, - - /** - * The {@link DataTransferItem} is for a file. - * - * Use {@link DataTransferItem.asFile} to get the underlying file data. - */ - File = 2, - } - - export interface DataTransferItem { - - /** - * The kind of the {@link DataTransferItem}. - */ - readonly kind: DataTransferItemKind; - - /** - * Try getting the file associated with this data transfer item. - * - * Note that the file object is only valid for the scope of the drag and drop operation. - * - * @returns The file for the data transfer or `undefined` if the item is either not a file or the - * file data cannot be accessed. - */ - asFile(): DataTransferFile | undefined; - } -} From b71f44bab23c1758b2f94bc3572e0573068c9295 Mon Sep 17 00:00:00 2001 From: "Babak K. Shandiz" Date: Wed, 15 Jun 2022 14:55:13 +0000 Subject: [PATCH 048/175] =?UTF-8?q?=F0=9F=94=A8=20Fix=20inactive=20asserti?= =?UTF-8?q?ons=20on=20choice=20options?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Babak K. Shandiz --- .../snippet/test/browser/snippetParser.test.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts index 13533bed041..b36e7f8b570 100644 --- a/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts +++ b/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts @@ -273,12 +273,13 @@ suite('SnippetParser', () => { assertTextAndMarker('${1|one,two,three,|}', '${1|one,two,three,|}', Text); assertTextAndMarker('${1|one,', '${1|one,', Text); - const p = new SnippetParser(); - const snippet = p.parse('${1|one,two,three|}'); - assertMarker(snippet, Placeholder); - const expected = [Placeholder, Text, Text, Text]; + const snippet = new SnippetParser().parse('${1|one,two,three|}'); + const expected: ((m: Marker) => boolean)[] = [ + m => m instanceof Placeholder, + m => m instanceof Choice && m.options.length === 3 && m.options.every(x => x instanceof Text), + ]; snippet.walk(marker => { - assert.strictEqual(marker, expected.shift()); + assert.ok(expected.shift()!(marker)); return true; }); }); From 4e0f15a47b2b448e67d4ca6e50c979f5cf2e99a5 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 15 Jun 2022 17:06:16 +0200 Subject: [PATCH 049/175] Use dispose utility in commenting feature area (#152210) --- .../comments/browser/commentThreadRangeDecorator.ts | 6 +++--- .../contrib/comments/browser/commentThreadWidget.ts | 4 ++-- .../comments/browser/commentsEditorContribution.ts | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadRangeDecorator.ts b/src/vs/workbench/contrib/comments/browser/commentThreadRangeDecorator.ts index 73cdeb596fd..973762aa995 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadRangeDecorator.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadRangeDecorator.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IRange } from 'vs/editor/common/core/range'; import { CommentThread, CommentThreadCollapsibleState } from 'vs/editor/common/languages'; @@ -93,7 +93,7 @@ export class CommentThreadRangeDecorator extends Disposable { if (!model) { return; } - this.threadCollapseStateListeners.forEach(disposable => disposable.dispose()); + dispose(this.threadCollapseStateListeners); this.editor = editor; const commentThreadRangeDecorations: CommentThreadRangeDecoration[] = []; @@ -127,7 +127,7 @@ export class CommentThreadRangeDecorator extends Disposable { } override dispose() { - this.threadCollapseStateListeners.forEach(disposable => disposable.dispose()); + dispose(this.threadCollapseStateListeners); this.currentThreadCollapseStateListener?.dispose(); super.dispose(); } diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts index 1da1f2064d7..c5fd1206a96 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/review'; import * as dom from 'vs/base/browser/dom'; import { Emitter } from 'vs/base/common/event'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import * as languages from 'vs/editor/common/languages'; import { IMarkdownRendererOptions } from 'vs/editor/contrib/markdownRenderer/browser/markdownRenderer'; @@ -148,7 +148,7 @@ export class CommentThreadWidget extends updateCommentThread(commentThread: languages.CommentThread) { if (this._commentThread !== commentThread) { - this._commentThreadDisposables.forEach(disposable => disposable.dispose()); + dispose(this._commentThreadDisposables); } this._commentThread = commentThread; diff --git a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts index 606f1e77fd8..54af2c8fa32 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts @@ -9,7 +9,7 @@ import { coalesce, findFirstInSorted } from 'vs/base/common/arrays'; import { CancelablePromise, createCancelablePromise, Delayer } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; import 'vs/css!./media/review'; import { IActiveCodeEditor, ICodeEditor, IEditorMouseEvent, isCodeEditor, isDiffEditor, IViewZone } from 'vs/editor/browser/editorBrowser'; import { EditorAction, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; @@ -310,7 +310,7 @@ export class CommentController implements IEditorContribution { private _computeCommentingRangePromise!: CancelablePromise | null; private _computeCommentingRangeScheduler!: Delayer> | null; private _pendingCommentCache: { [key: string]: { [key: string]: string } }; - private _editorDisposables: IDisposable[] | undefined; + private _editorDisposables: IDisposable[] = []; private _activeCursorHasCommentingRange: IContextKey; constructor( @@ -340,7 +340,7 @@ export class CommentController implements IEditorContribution { this.globalToDispose.add(this._commentingRangeDecorator.onDidChangeDecorationsCount(count => { if (count === 0) { this.clearEditorListeners(); - } else if (!this._editorDisposables) { + } else if (this._editorDisposables.length === 0) { this.registerEditorListeners(); } })); @@ -376,8 +376,8 @@ export class CommentController implements IEditorContribution { } private clearEditorListeners() { - this._editorDisposables?.forEach(disposable => disposable.dispose()); - this._editorDisposables = undefined; + dispose(this._editorDisposables); + this._editorDisposables = []; } private onEditorMouseMove(e: IEditorMouseEvent): void { @@ -556,7 +556,7 @@ export class CommentController implements IEditorContribution { this.localToDispose.add(this.editor.onMouseDown(e => this.onEditorMouseDown(e))); this.localToDispose.add(this.editor.onMouseUp(e => this.onEditorMouseUp(e))); - if (this._editorDisposables) { + if (this._editorDisposables.length) { this.clearEditorListeners(); this.registerEditorListeners(); } From 9cd97eea68bd4364365b96078caf2e5512f26bcd Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 15 Jun 2022 08:16:02 -0700 Subject: [PATCH 050/175] Deprecate collections.forEach (#152132) This marks `collections.forEach` as deprecated. We should use `Object.entries` instead I've also removed the `remove` functionality since no callers were using it and updated the implementation to use `Object.entries` --- src/vs/base/common/collections.ts | 19 ++++++++----------- src/vs/base/test/common/collections.test.ts | 3 --- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/vs/base/common/collections.ts b/src/vs/base/common/collections.ts index b3435048211..190492f58f7 100644 --- a/src/vs/base/common/collections.ts +++ b/src/vs/base/common/collections.ts @@ -33,18 +33,15 @@ export function values(from: IStringDictionary | INumberDictionary): T[ } /** - * Iterates over each entry in the provided dictionary. The iterator allows - * to remove elements and will stop when the callback returns {{false}}. + * Iterates over each entry in the provided dictionary. The iterator will stop when the callback returns `false`. + * + * @deprecated Use `Object.entries(x)` with a `for...of` loop. */ -export function forEach(from: IStringDictionary | INumberDictionary, callback: (entry: { key: any; value: T }, remove: () => void) => any): void { - for (const key in from) { - if (hasOwnProperty.call(from, key)) { - const result = callback({ key: key, value: (from as any)[key] }, function () { - delete (from as any)[key]; - }); - if (result === false) { - return; - } +export function forEach(from: IStringDictionary | INumberDictionary, callback: (entry: { key: any; value: T }) => any): void { + for (const [key, value] of Object.entries(from)) { + const result = callback({ key, value }); + if (result === false) { + return; } } } diff --git a/src/vs/base/test/common/collections.test.ts b/src/vs/base/test/common/collections.test.ts index 2cae56c40ef..138d7486390 100644 --- a/src/vs/base/test/common/collections.test.ts +++ b/src/vs/base/test/common/collections.test.ts @@ -24,9 +24,6 @@ suite('Collections', () => { collections.forEach(dict, () => false); - collections.forEach(dict, (x, remove) => remove()); - assert.strictEqual(dict['toString'], undefined); - // don't iterate over properties that are not on the object itself const test = Object.create({ 'derived': true }); collections.forEach(test, () => assert(false)); From 342b0f116456be22dfe5bb01db12aa0075d6da96 Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 15 Jun 2022 17:34:45 +0200 Subject: [PATCH 051/175] don't wrap `menubar` inside the titlebar part --- src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 3f0663fa1ba..166650a2705 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -162,6 +162,7 @@ /* move menubar above drag region as negative z-index on drag region cause greyscale AA */ z-index: 2500; min-width: 36px; + flex-wrap: nowrap; } /* Resizer */ From b30e46bc50061ee485b4b872570a5d70e8766e1d Mon Sep 17 00:00:00 2001 From: Qingpeng Li Date: Wed, 15 Jun 2022 23:36:21 +0800 Subject: [PATCH 052/175] Fix symbolkind icon --- .../workbench/contrib/callHierarchy/browser/callHierarchyTree.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts index 6ea920674a5..5175aa33d6d 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts @@ -119,6 +119,7 @@ export class CallRenderer implements ITreeRenderer, _index: number, template: CallRenderingTemplate): void { const { element, filterData } = node; const deprecated = element.item.tags?.includes(SymbolTag.Deprecated); + template.icon.className = ''; template.icon.classList.add('inline', ...CSSIcon.asClassNameArray(SymbolKinds.toIcon(element.item.kind))); template.label.setLabel( element.item.name, From 2caf5e25274ab8a8de7f14c057c0db2b918ba4cd Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 15 Jun 2022 17:38:43 +0200 Subject: [PATCH 053/175] :lipstick: --- .../workbench/browser/parts/titlebar/media/titlebarpart.css | 1 - src/vs/workbench/browser/parts/titlebar/titlebarPart.ts | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 166650a2705..0df287925b9 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -41,7 +41,6 @@ .monaco-workbench.web .part.titlebar>.titlebar-container, .monaco-workbench.windows .part.titlebar>.titlebar-container, .monaco-workbench.linux .part.titlebar>.titlebar-container { - /* height: 30px; */ line-height: 22px; justify-content: left; } diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index d89e7ea1fb5..388be333154 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -46,8 +46,8 @@ export class TitlebarPart extends Part implements ITitleService { readonly minimumWidth: number = 0; readonly maximumWidth: number = Number.POSITIVE_INFINITY; get minimumHeight(): number { - const ccPadding = this.isCommandCenterVisible ? 5 : 0; - return (30 + ccPadding) / (this.currentMenubarVisibility === 'hidden' || getZoomFactor() < 1 ? getZoomFactor() : 1); + const value = this.isCommandCenterVisible ? 35 : 30; + return value / (this.currentMenubarVisibility === 'hidden' || getZoomFactor() < 1 ? getZoomFactor() : 1); } get maximumHeight(): number { return this.minimumHeight; } From dfc37187b01255dcad2107d03eec6b5db80e6c2b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 15 Jun 2022 17:58:11 +0200 Subject: [PATCH 054/175] rename ref-viewlet's publisher back to `vscode` (#152213) --- extensions/references-view/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/references-view/package.json b/extensions/references-view/package.json index 50d084e2751..f5c0a910f1e 100644 --- a/extensions/references-view/package.json +++ b/extensions/references-view/package.json @@ -4,7 +4,7 @@ "description": "%description%", "icon": "media/icon.png", "version": "1.0.0", - "publisher": "ms-vscode", + "publisher": "vscode", "license": "MIT", "engines": { "vscode": "^1.67.0" From f17b33faf21feba942e4663e40226456b081ae5d Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 15 Jun 2022 09:28:31 -0700 Subject: [PATCH 055/175] Use `.?method()` in more places (#152112) Switches simple patterns like: ```ts if (some.thing) { some.thing.method(); } ``` to: ```ts some.thing?.method() ``` This is more concise and avoids having to repeat the `some.thing` part --- extensions/git/src/util.ts | 4 +- .../preview-src/loading.ts | 6 +-- .../merge-conflict/src/documentTracker.ts | 4 +- src/vs/base/browser/browser.ts | 4 +- src/vs/base/browser/defaultWorkerFactory.ts | 8 +--- src/vs/base/browser/ui/dialog/dialog.ts | 4 +- src/vs/base/browser/ui/findinput/findInput.ts | 4 +- .../base/browser/ui/findinput/replaceInput.ts | 8 +--- src/vs/base/browser/ui/list/listView.ts | 4 +- src/vs/base/browser/ui/list/listWidget.ts | 4 +- src/vs/base/browser/ui/list/rowCache.ts | 4 +- src/vs/base/browser/ui/menu/menu.ts | 8 +--- .../scrollbarVisibilityController.ts | 8 +--- .../browser/ui/selectBox/selectBoxCustom.ts | 4 +- src/vs/base/browser/ui/toolbar/toolbar.ts | 4 +- src/vs/base/browser/ui/tree/abstractTree.ts | 4 +- src/vs/base/common/event.ts | 12 ++---- src/vs/base/common/scrollable.ts | 4 +- src/vs/base/parts/ipc/common/ipc.ts | 16 ++------ src/vs/base/parts/storage/common/storage.ts | 8 +--- .../issue/issueReporterMain.ts | 4 +- .../editor/browser/config/fontMeasurements.ts | 4 +- .../browser/controller/textAreaHandler.ts | 4 +- .../browser/viewParts/minimap/minimap.ts | 8 +--- .../editor/browser/widget/codeEditorWidget.ts | 4 +- .../editor/browser/widget/diffEditorWidget.ts | 8 +--- .../bracketPairsTree/bracketPairsTree.ts | 8 +--- .../editor/common/services/languageService.ts | 4 +- .../services/markerDecorationsService.ts | 4 +- .../codeAction/browser/codeActionCommands.ts | 4 +- .../codeAction/browser/codeActionModel.ts | 4 +- .../codelens/browser/codelensWidget.ts | 4 +- .../editor/contrib/folding/browser/folding.ts | 4 +- .../common/model/textModelWithTokens.test.ts | 4 +- .../common/configurationRegistry.ts | 8 +--- .../contextview/browser/contextMenuHandler.ts | 4 +- .../common/serviceMachineId.ts | 6 +-- .../browser/indexedDBFileSystemProvider.ts | 4 +- src/vs/platform/log/common/bufferLog.ts | 4 +- src/vs/platform/log/node/spdlogLog.ts | 4 +- .../electron-main/nativeHostMainService.ts | 16 ++------ .../platform/quickinput/browser/quickInput.ts | 4 +- .../storage/electron-main/storageIpc.ts | 4 +- .../telemetry/browser/errorTelemetry.ts | 4 +- .../common/xterm/shellIntegrationAddon.ts | 4 +- .../userDataSync/common/extensionsMerge.ts | 4 +- .../electron-main/windowsMainService.ts | 8 +--- src/vs/server/node/extensionHostConnection.ts | 4 +- .../api/browser/mainThreadDebugService.ts | 8 +--- .../api/browser/mainThreadLanguageFeatures.ts | 4 +- .../api/browser/mainThreadNotebookKernels.ts | 8 +--- .../api/browser/mainThreadTerminalService.ts | 4 +- .../api/common/extHostDebugService.ts | 8 +--- .../api/common/extHostDiagnostics.ts | 4 +- .../api/common/extHostLanguageFeatures.ts | 4 +- .../workbench/api/common/extHostQuickOpen.ts | 8 +--- .../api/common/extHostTerminalService.ts | 8 +--- src/vs/workbench/api/common/extHostTypes.ts | 4 +- src/vs/workbench/api/node/extHostSearch.ts | 4 +- .../workbench/api/node/extHostStoragePaths.ts | 4 +- .../browser/actions/developerActions.ts | 4 +- .../parts/activitybar/activitybarPart.ts | 8 +--- .../workbench/browser/parts/compositeBar.ts | 8 +--- .../workbench/browser/parts/compositePart.ts | 12 ++---- .../browser/parts/editor/editorCommands.ts | 4 +- .../browser/parts/editor/editorPart.ts | 8 +--- .../browser/parts/panel/panelPart.ts | 4 +- .../browser/parts/titlebar/menubarControl.ts | 8 +--- .../workbench/browser/parts/views/treeView.ts | 8 +--- .../common/editor/textResourceEditorInput.ts | 4 +- .../codeEditor/browser/saveParticipants.ts | 4 +- .../contrib/comments/browser/commentNode.ts | 8 +--- .../comments/browser/commentService.ts | 4 +- .../browser/commentsEditorContribution.ts | 4 +- .../configurationExportHelper.ts | 4 +- .../contrib/debug/browser/breakpointsView.ts | 4 +- .../contrib/debug/browser/debugSession.ts | 8 +--- .../contrib/debug/browser/debugStatus.ts | 4 +- .../contrib/debug/browser/disassemblyView.ts | 4 +- .../debug/common/debugContentProvider.ts | 4 +- .../contrib/debug/node/debugAdapter.ts | 4 +- .../experiments/browser/experimentalPrompt.ts | 4 +- .../abstractRuntimeExtensionsEditor.ts | 8 +--- .../extensions/browser/extensionEditor.ts | 8 +--- .../extensions/browser/extensionsActions.ts | 4 +- .../extensions/browser/extensionsViewlet.ts | 4 +- .../extensions/browser/extensionsViews.ts | 4 +- .../extensionProfileService.ts | 4 +- .../contrib/files/browser/explorerViewlet.ts | 4 +- .../files/browser/views/explorerView.ts | 8 +--- .../files/browser/views/openEditorsView.ts | 4 +- .../contrib/files/common/explorerModel.ts | 16 ++------ .../markers/browser/markersTreeViewer.ts | 4 +- .../contrib/markers/browser/markersView.ts | 4 +- .../notebook/browser/notebookEditor.ts | 4 +- .../browser/view/cellParts/markdownCell.ts | 4 +- .../common/services/notebookSimpleWorker.ts | 4 +- .../contrib/output/browser/outputServices.ts | 4 +- .../contrib/output/browser/outputView.ts | 4 +- .../performance/browser/perfviewEditor.ts | 4 +- .../preferences/browser/preferencesWidgets.ts | 4 +- .../preferences/browser/settingsEditor2.ts | 4 +- .../preferences/browser/settingsTreeModels.ts | 4 +- .../contrib/search/browser/searchActions.ts | 4 +- .../contrib/search/browser/searchView.ts | 12 ++---- .../contrib/search/common/searchModel.ts | 12 ++---- .../search/test/common/searchModel.test.ts | 4 +- .../tasks/browser/terminalTaskSystem.ts | 12 ++---- .../contrib/tasks/common/problemCollectors.ts | 4 +- .../terminal/browser/terminalFindWidget.ts | 4 +- .../contrib/terminal/browser/terminalGroup.ts | 4 +- .../browser/terminalMainContribution.ts | 4 +- .../browser/gettingStartedList.ts | 4 +- .../browser/configurationService.ts | 4 +- .../common/variableResolver.ts | 4 +- .../browser/webWorkerExtensionHost.ts | 4 +- .../services/extensions/common/rpcProtocol.ts | 40 +++++-------------- .../nativeLocalProcessExtensionHost.ts | 4 +- .../preferences/common/preferencesModels.ts | 4 +- .../services/search/common/searchService.ts | 8 +--- .../services/search/node/rawSearchService.ts | 4 +- .../textMate/browser/textMateWorker.ts | 4 +- 122 files changed, 179 insertions(+), 529 deletions(-) diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts index 53ab6c9e310..e36cebf97cf 100644 --- a/extensions/git/src/util.ts +++ b/extensions/git/src/util.ts @@ -51,9 +51,7 @@ export function anyEvent(...events: Event[]): Event { return (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => { const result = combinedDisposable(events.map(event => event(i => listener.call(thisArgs, i)))); - if (disposables) { - disposables.push(result); - } + disposables?.push(result); return result; }; diff --git a/extensions/markdown-language-features/preview-src/loading.ts b/extensions/markdown-language-features/preview-src/loading.ts index fea2d653d4d..c6439188766 100644 --- a/extensions/markdown-language-features/preview-src/loading.ts +++ b/extensions/markdown-language-features/preview-src/loading.ts @@ -29,9 +29,7 @@ export class StyleLoadingMonitor { return; } this.finishedLoading = true; - if (this.poster) { - this.poster.postMessage('previewStyleLoadError', { unloadedStyles: this.unloadedStyles }); - } + this.poster?.postMessage('previewStyleLoadError', { unloadedStyles: this.unloadedStyles }); }); } @@ -41,4 +39,4 @@ export class StyleLoadingMonitor { poster.postMessage('previewStyleLoadError', { unloadedStyles: this.unloadedStyles }); } } -} \ No newline at end of file +} diff --git a/extensions/merge-conflict/src/documentTracker.ts b/extensions/merge-conflict/src/documentTracker.ts index d6247b20671..f52644ab9df 100644 --- a/extensions/merge-conflict/src/documentTracker.ts +++ b/extensions/merge-conflict/src/documentTracker.ts @@ -69,9 +69,7 @@ export default class DocumentMergeConflictTracker implements vscode.Disposable, return cacheItem.delayTask.trigger(() => { const conflicts = this.getConflictsOrEmpty(document, Array.from(cacheItem!.origins)); - if (this.cache) { - this.cache.delete(key!); - } + this.cache?.delete(key!); return conflicts; }); diff --git a/src/vs/base/browser/browser.ts b/src/vs/base/browser/browser.ts index d41cafcdb77..8191f523595 100644 --- a/src/vs/base/browser/browser.ts +++ b/src/vs/base/browser/browser.ts @@ -71,9 +71,7 @@ class DevicePixelRatioMonitor extends Disposable { } private _handleChange(fireEvent: boolean): void { - if (this._mediaQueryList) { - this._mediaQueryList.removeEventListener('change', this._listener); - } + this._mediaQueryList?.removeEventListener('change', this._listener); this._mediaQueryList = matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`); this._mediaQueryList.addEventListener('change', this._listener); diff --git a/src/vs/base/browser/defaultWorkerFactory.ts b/src/vs/base/browser/defaultWorkerFactory.ts index 93b87e6a563..c57e67f3698 100644 --- a/src/vs/base/browser/defaultWorkerFactory.ts +++ b/src/vs/base/browser/defaultWorkerFactory.ts @@ -86,15 +86,11 @@ class WebWorker implements IWorker { } public postMessage(message: any, transfer: Transferable[]): void { - if (this.worker) { - this.worker.then(w => w.postMessage(message, transfer)); - } + this.worker?.then(w => w.postMessage(message, transfer)); } public dispose(): void { - if (this.worker) { - this.worker.then(w => w.terminate()); - } + this.worker?.then(w => w.terminate()); this.worker = null; } } diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts index 0643cf674a6..4076b02b51e 100644 --- a/src/vs/base/browser/ui/dialog/dialog.ts +++ b/src/vs/base/browser/ui/dialog/dialog.ts @@ -431,9 +431,7 @@ export class Dialog extends Disposable { this.buttonBar.buttons.forEach(button => button.style(style)); } - if (this.checkbox) { - this.checkbox.style(style); - } + this.checkbox?.style(style); if (fgColor && bgColor) { const messageDetailColor = fgColor.transparent(.9); diff --git a/src/vs/base/browser/ui/findinput/findInput.ts b/src/vs/base/browser/ui/findinput/findInput.ts index 4c40cba159f..b9e218ce03b 100644 --- a/src/vs/base/browser/ui/findinput/findInput.ts +++ b/src/vs/base/browser/ui/findinput/findInput.ts @@ -252,9 +252,7 @@ export class FindInput extends Widget { this.domNode.appendChild(this.controls); - if (parent) { - parent.appendChild(this.domNode); - } + parent?.appendChild(this.domNode); this._register(dom.addDisposableListener(this.inputBox.inputElement, 'compositionstart', (e: CompositionEvent) => { this.imeSessionInProgress = true; diff --git a/src/vs/base/browser/ui/findinput/replaceInput.ts b/src/vs/base/browser/ui/findinput/replaceInput.ts index 46b4014bb3b..a80c5a1faaf 100644 --- a/src/vs/base/browser/ui/findinput/replaceInput.ts +++ b/src/vs/base/browser/ui/findinput/replaceInput.ts @@ -225,9 +225,7 @@ export class ReplaceInput extends Widget { this.domNode.appendChild(controls); - if (parent) { - parent.appendChild(this.domNode); - } + parent?.appendChild(this.domNode); this.onkeydown(this.inputBox.inputElement, (e) => this._onKeyDown.fire(e)); this.onkeyup(this.inputBox.inputElement, (e) => this._onKeyUp.fire(e)); @@ -361,9 +359,7 @@ export class ReplaceInput extends Widget { } public showMessage(message: InputBoxMessage): void { - if (this.inputBox) { - this.inputBox.showMessage(message); - } + this.inputBox?.showMessage(message); } public clearMessage(): void { diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 0f5d1aaa0ea..8612a2f213a 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -813,9 +813,7 @@ export class ListView implements ISpliceable, IDisposable { throw new Error(`No renderer found for template id ${item.templateId}`); } - if (renderer) { - renderer.renderElement(item.element, index, item.row.templateData, item.size); - } + renderer?.renderElement(item.element, index, item.row.templateData, item.size); const uri = this.dnd.getDragURI(item.element); item.dragStartDisposable.dispose(); diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 8d509cb4da5..d477a7d3f88 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -1390,9 +1390,7 @@ export class List implements ISpliceable, IThemable, IDisposable { updateOptions(optionsUpdate: IListOptionsUpdate = {}): void { this._options = { ...this._options, ...optionsUpdate }; - if (this.typeLabelController) { - this.typeLabelController.updateOptions(this._options); - } + this.typeLabelController?.updateOptions(this._options); if (this._options.multipleSelectionController !== undefined) { if (this._options.multipleSelectionSupport) { diff --git a/src/vs/base/browser/ui/list/rowCache.ts b/src/vs/base/browser/ui/list/rowCache.ts index 319d839e7d7..a5c4b7e07c2 100644 --- a/src/vs/base/browser/ui/list/rowCache.ts +++ b/src/vs/base/browser/ui/list/rowCache.ts @@ -15,9 +15,7 @@ export interface IRow { function removeFromParent(element: HTMLElement): void { try { - if (element.parentElement) { - element.parentElement.removeChild(element); - } + element.parentElement?.removeChild(element); } catch (e) { // this will throw if this happens due to a blur event, nasty business } diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index 224b0cdcb73..7a22cb35038 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -609,9 +609,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem { this.label.innerText = replaceDoubleEscapes(label).trim(); } - if (this.item) { - this.item.setAttribute('aria-keyshortcuts', (!!matches[1] ? matches[1] : matches[3]).toLocaleLowerCase()); - } + this.item?.setAttribute('aria-keyshortcuts', (!!matches[1] ? matches[1] : matches[3]).toLocaleLowerCase()); } else { this.label.innerText = label.replace(/&&/g, '&').trim(); } @@ -966,9 +964,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { this.submenuIndicator.style.color = fgColor ? `${fgColor}` : ''; } - if (this.parentData.submenu) { - this.parentData.submenu.style(this.menuStyle); - } + this.parentData.submenu?.style(this.menuStyle); } override dispose(): void { diff --git a/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts b/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts index 3c4a40b824f..791c7332ffb 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts @@ -103,9 +103,7 @@ export class ScrollbarVisibilityController extends Disposable { // The CSS animation doesn't play otherwise this._revealTimer.setIfNotSet(() => { - if (this._domNode) { - this._domNode.setClassName(this._visibleClassName); - } + this._domNode?.setClassName(this._visibleClassName); }, 0); } @@ -115,8 +113,6 @@ export class ScrollbarVisibilityController extends Disposable { return; } this._isVisible = false; - if (this._domNode) { - this._domNode.setClassName(this._invisibleClassName + (withFadeAway ? ' fade' : '')); - } + this._domNode?.setClassName(this._invisibleClassName + (withFadeAway ? ' fade' : '')); } } diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index 903700ac0a8..e4f0a07538d 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -286,9 +286,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi // Mirror options in drop-down // Populate select list for non-native select mode - if (this.selectList) { - this.selectList.splice(0, this.selectList.length, this.options); - } + this.selectList?.splice(0, this.selectList.length, this.options); } public select(index: number): void { diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index 63941c5452e..e3c3a56066e 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -130,9 +130,7 @@ export class ToolBar extends Disposable { set context(context: unknown) { this.actionBar.context = context; - if (this.toggleMenuActionViewItem) { - this.toggleMenuActionViewItem.setActionContext(context); - } + this.toggleMenuActionViewItem?.setActionContext(context); for (const actionViewItem of this.submenuActionViewItems) { actionViewItem.setActionContext(context); } diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 7f96f25f3f2..c03f5c5e1af 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -1453,9 +1453,7 @@ export abstract class AbstractTree implements IDisposable enableKeyboardNavigation: this._options.simpleKeyboardNavigation, }); - if (this.typeFilterController) { - this.typeFilterController.updateOptions(this._options); - } + this.typeFilterController?.updateOptions(this._options); this._onDidUpdateOptions.fire(this._options); diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index bd8ef3bbedb..647b5f1beed 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -160,9 +160,7 @@ export namespace Event { const emitter = new Emitter(options); - if (disposable) { - disposable.add(emitter); - } + disposable?.add(emitter); return emitter.event; } @@ -223,9 +221,7 @@ export namespace Event { const emitter = new Emitter(options); - if (disposable) { - disposable.add(emitter); - } + disposable?.add(emitter); return emitter.event; } @@ -276,9 +272,7 @@ export namespace Event { }); const flush = () => { - if (buffer) { - buffer.forEach(e => emitter.fire(e)); - } + buffer?.forEach(e => emitter.fire(e)); buffer = null; }; diff --git a/src/vs/base/common/scrollable.ts b/src/vs/base/common/scrollable.ts index 7da28a8555d..89c832edcd5 100644 --- a/src/vs/base/common/scrollable.ts +++ b/src/vs/base/common/scrollable.ts @@ -268,9 +268,7 @@ export class Scrollable extends Disposable { this._setState(newState, Boolean(this._smoothScrolling)); // Validate outstanding animated scroll position target - if (this._smoothScrolling) { - this._smoothScrolling.acceptScrollDimensions(this._state); - } + this._smoothScrolling?.acceptScrollDimensions(this._state); } /** diff --git a/src/vs/base/parts/ipc/common/ipc.ts b/src/vs/base/parts/ipc/common/ipc.ts index 42033ee91aa..93f944934f3 100644 --- a/src/vs/base/parts/ipc/common/ipc.ts +++ b/src/vs/base/parts/ipc/common/ipc.ts @@ -640,18 +640,14 @@ export class ChannelClient implements IChannelClient, IDisposable { case RequestType.Promise: case RequestType.EventListen: { const msgLength = this.send([request.type, request.id, request.channelName, request.name], request.arg); - if (this.logger) { - this.logger.logOutgoing(msgLength, request.id, RequestInitiator.LocalSide, `${requestTypeToStr(request.type)}: ${request.channelName}.${request.name}`, request.arg); - } + this.logger?.logOutgoing(msgLength, request.id, RequestInitiator.LocalSide, `${requestTypeToStr(request.type)}: ${request.channelName}.${request.name}`, request.arg); return; } case RequestType.PromiseCancel: case RequestType.EventDispose: { const msgLength = this.send([request.type, request.id]); - if (this.logger) { - this.logger.logOutgoing(msgLength, request.id, RequestInitiator.LocalSide, requestTypeToStr(request.type)); - } + this.logger?.logOutgoing(msgLength, request.id, RequestInitiator.LocalSide, requestTypeToStr(request.type)); return; } } @@ -682,18 +678,14 @@ export class ChannelClient implements IChannelClient, IDisposable { switch (type) { case ResponseType.Initialize: - if (this.logger) { - this.logger.logIncoming(message.byteLength, 0, RequestInitiator.LocalSide, responseTypeToStr(type)); - } + this.logger?.logIncoming(message.byteLength, 0, RequestInitiator.LocalSide, responseTypeToStr(type)); return this.onResponse({ type: header[0] }); case ResponseType.PromiseSuccess: case ResponseType.PromiseError: case ResponseType.EventFire: case ResponseType.PromiseErrorObj: - if (this.logger) { - this.logger.logIncoming(message.byteLength, header[1], RequestInitiator.LocalSide, responseTypeToStr(type), body); - } + this.logger?.logIncoming(message.byteLength, header[1], RequestInitiator.LocalSide, responseTypeToStr(type), body); return this.onResponse({ type: header[0], id: header[1], data: body }); } } diff --git a/src/vs/base/parts/storage/common/storage.ts b/src/vs/base/parts/storage/common/storage.ts index a6bfeaefbd9..8e42b693a93 100644 --- a/src/vs/base/parts/storage/common/storage.ts +++ b/src/vs/base/parts/storage/common/storage.ts @@ -357,13 +357,9 @@ export class InMemoryStorageDatabase implements IStorageDatabase { } async updateItems(request: IUpdateRequest): Promise { - if (request.insert) { - request.insert.forEach((value, key) => this.items.set(key, value)); - } + request.insert?.forEach((value, key) => this.items.set(key, value)); - if (request.delete) { - request.delete.forEach(key => this.items.delete(key)); - } + request.delete?.forEach(key => this.items.delete(key)); } async close(): Promise { } diff --git a/src/vs/code/electron-sandbox/issue/issueReporterMain.ts b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts index ff591f90b72..8b62af66a09 100644 --- a/src/vs/code/electron-sandbox/issue/issueReporterMain.ts +++ b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts @@ -1197,9 +1197,7 @@ export class IssueReporter extends Disposable { private addEventListener(elementId: string, eventType: string, handler: (event: Event) => void): void { const element = this.getElementById(elementId); - if (element) { - element.addEventListener(eventType, handler); - } + element?.addEventListener(eventType, handler); } } diff --git a/src/vs/editor/browser/config/fontMeasurements.ts b/src/vs/editor/browser/config/fontMeasurements.ts index 39e1737cd33..00dde46dcc9 100644 --- a/src/vs/editor/browser/config/fontMeasurements.ts +++ b/src/vs/editor/browser/config/fontMeasurements.ts @@ -149,9 +149,7 @@ class FontMeasurementsImpl extends Disposable { private _createRequest(chr: string, type: CharWidthRequestType, all: CharWidthRequest[], monospace: CharWidthRequest[] | null): CharWidthRequest { const result = new CharWidthRequest(chr, type); all.push(result); - if (monospace) { - monospace.push(result); - } + monospace?.push(result); return result; } diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index ffbe3b55882..45970c2a2b6 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -634,9 +634,7 @@ export class TextAreaHandler extends ViewPart { public prepareRender(ctx: RenderingContext): void { this._primaryCursorPosition = new Position(this._selections[0].positionLineNumber, this._selections[0].positionColumn); this._primaryCursorVisibleRange = ctx.visibleRangeForPosition(this._primaryCursorPosition); - if (this._visibleTextArea) { - this._visibleTextArea.prepareRender(ctx); - } + this._visibleTextArea?.prepareRender(ctx); } public render(ctx: RestrictedRenderingContext): void { diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 2edb031b356..2b1ac590d2b 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -1325,15 +1325,11 @@ class InnerMinimap extends Disposable { return false; } public onLinesDeleted(deleteFromLineNumber: number, deleteToLineNumber: number): boolean { - if (this._lastRenderData) { - this._lastRenderData.onLinesDeleted(deleteFromLineNumber, deleteToLineNumber); - } + this._lastRenderData?.onLinesDeleted(deleteFromLineNumber, deleteToLineNumber); return true; } public onLinesInserted(insertFromLineNumber: number, insertToLineNumber: number): boolean { - if (this._lastRenderData) { - this._lastRenderData.onLinesInserted(insertFromLineNumber, insertToLineNumber); - } + this._lastRenderData?.onLinesInserted(insertFromLineNumber, insertToLineNumber); return true; } public onScrollChanged(): boolean { diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 1a4601d7434..43e5e06a4d7 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -1786,9 +1786,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } protected _postDetachModelCleanup(detachedModel: ITextModel | null): void { - if (detachedModel) { - detachedModel.removeAllDecorationsWithOwnerId(this._id); - } + detachedModel?.removeAllDecorationsWithOwnerId(this._id); } private _detachModel(): ITextModel | null { diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index 209299b7a71..59cd4e9918a 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -150,17 +150,13 @@ class VisualEditorState { } }); - if (scrollState) { - scrollState.restore(editor); - } + scrollState?.restore(editor); // decorations this._decorations = editor.deltaDecorations(this._decorations, newDecorations.decorations); // overview ruler - if (overviewRuler) { - overviewRuler.setZones(newDecorations.overviewZones); - } + overviewRuler?.setZones(newDecorations.overviewZones); } } diff --git a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/bracketPairsTree.ts b/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/bracketPairsTree.ts index 39c24d84072..d4485d47cc4 100644 --- a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/bracketPairsTree.ts +++ b/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/bracketPairsTree.ts @@ -304,9 +304,7 @@ function collectBrackets( nodeOffsetStart = nodeOffsetEnd; } - if (levelPerBracketType) { - levelPerBracketType.set(node.openingBracket.text, levelPerBracket); - } + levelPerBracketType?.set(node.openingBracket.text, levelPerBracket); } else if (node.kind === AstNodeKind.UnexpectedClosingBracket) { const range = lengthsToRange(nodeOffsetStart, nodeOffsetEnd); result.push(new BracketInfo(range, level - 1, 0, true)); @@ -394,9 +392,7 @@ function collectBracketPairs( } } - if (levelPerBracketType) { - levelPerBracketType.set(node.openingBracket.text, levelPerBracket); - } + levelPerBracketType?.set(node.openingBracket.text, levelPerBracket); } else { let curOffset = nodeOffsetStart; for (const child of node.children) { diff --git a/src/vs/editor/common/services/languageService.ts b/src/vs/editor/common/services/languageService.ts index 9dbbcda9b42..431317288bc 100644 --- a/src/vs/editor/common/services/languageService.ts +++ b/src/vs/editor/common/services/languageService.ts @@ -180,8 +180,6 @@ class LanguageSelection implements ILanguageSelection { return; } this.languageId = languageId; - if (this._emitter) { - this._emitter.fire(this.languageId); - } + this._emitter?.fire(this.languageId); } } diff --git a/src/vs/editor/common/services/markerDecorationsService.ts b/src/vs/editor/common/services/markerDecorationsService.ts index 95ade426458..15893914318 100644 --- a/src/vs/editor/common/services/markerDecorationsService.ts +++ b/src/vs/editor/common/services/markerDecorationsService.ts @@ -121,9 +121,7 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor if (model.uri.scheme === Schemas.inMemory || model.uri.scheme === Schemas.internal || model.uri.scheme === Schemas.vscode) { - if (this._markerService) { - this._markerService.read({ resource: model.uri }).map(marker => marker.owner).forEach(owner => this._markerService.remove(owner, [model.uri])); - } + this._markerService?.read({ resource: model.uri }).map(marker => marker.owner).forEach(owner => this._markerService.remove(owner, [model.uri])); } } diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/browser/codeActionCommands.ts index 7be66230fcc..7a4920f5dd6 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionCommands.ts @@ -244,9 +244,7 @@ function triggerCodeActionsForEditorSelection( ): void { if (editor.hasModel()) { const controller = QuickFixController.get(editor); - if (controller) { - controller.manualTriggerAtCurrentPosition(notAvailableMessage, filter, autoApply, preview); - } + controller?.manualTriggerAtCurrentPosition(notAvailableMessage, filter, autoApply, preview); } } diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts b/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts index f0a781143ae..6eb46ebab45 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts @@ -263,9 +263,7 @@ export class CodeActionModel extends Disposable { } public trigger(trigger: CodeActionTrigger) { - if (this._codeActionOracle.value) { - this._codeActionOracle.value.trigger(trigger); - } + this._codeActionOracle.value?.trigger(trigger); } private setState(newState: CodeActionsState.State, skipNotify?: boolean) { diff --git a/src/vs/editor/contrib/codelens/browser/codelensWidget.ts b/src/vs/editor/contrib/codelens/browser/codelensWidget.ts index bca6f3aa72a..d282350bfe6 100644 --- a/src/vs/editor/contrib/codelens/browser/codelensWidget.ts +++ b/src/vs/editor/contrib/codelens/browser/codelensWidget.ts @@ -254,9 +254,7 @@ export class CodeLensWidget { dispose(helper: CodeLensHelper, viewZoneChangeAccessor?: IViewZoneChangeAccessor): void { this._decorationIds.forEach(helper.removeDecoration, helper); this._decorationIds = []; - if (viewZoneChangeAccessor) { - viewZoneChangeAccessor.removeZone(this._viewZoneId); - } + viewZoneChangeAccessor?.removeZone(this._viewZoneId); if (this._contentWidget) { this._editor.removeContentWidget(this._contentWidget); this._contentWidget = undefined; diff --git a/src/vs/editor/contrib/folding/browser/folding.ts b/src/vs/editor/contrib/folding/browser/folding.ts index aa85b0fa29b..391249b2436 100644 --- a/src/vs/editor/contrib/folding/browser/folding.ts +++ b/src/vs/editor/contrib/folding/browser/folding.ts @@ -355,9 +355,7 @@ export class FoldingController extends Disposable implements IEditorContribution const selectionLineNumbers = selections ? selections.map(s => s.startLineNumber) : []; foldingModel.update(foldingRanges, selectionLineNumbers); - if (scrollState) { - scrollState.restore(this.editor); - } + scrollState?.restore(this.editor); // update debounce info const newValue = this.updateDebounceInfo.update(foldingModel.textModel, sw.elapsed()); diff --git a/src/vs/editor/test/common/model/textModelWithTokens.test.ts b/src/vs/editor/test/common/model/textModelWithTokens.test.ts index 30a4da8ee9f..4d4bfcafcc4 100644 --- a/src/vs/editor/test/common/model/textModelWithTokens.test.ts +++ b/src/vs/editor/test/common/model/textModelWithTokens.test.ts @@ -163,9 +163,7 @@ function assertIsNotBracket(model: TextModel, lineNumber: number, column: number function assertIsBracket(model: TextModel, testPosition: Position, expected: [Range, Range]): void { expected.sort(Range.compareRangesUsingStarts); const actual = model.bracketPairs.matchBracket(testPosition); - if (actual) { - actual.sort(Range.compareRangesUsingStarts); - } + actual?.sort(Range.compareRangesUsingStarts); assert.deepStrictEqual(actual, expected, 'matches brackets at ' + testPosition); } diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index f945cf1b18c..63347d8f2a7 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -425,9 +425,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { this.removeFromSchema(key, configuration.properties[key]); } } - if (configuration.allOf) { - configuration.allOf.forEach(node => deregisterConfiguration(node)); - } + configuration.allOf?.forEach(node => deregisterConfiguration(node)); }; for (const configuration of configurations) { deregisterConfiguration(configuration); @@ -525,9 +523,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { } } const subNodes = configuration.allOf; - if (subNodes) { - subNodes.forEach(register); - } + subNodes?.forEach(register); }; register(configuration); } diff --git a/src/vs/platform/contextview/browser/contextMenuHandler.ts b/src/vs/platform/contextview/browser/contextMenuHandler.ts index 8458bdcb22f..aa6612d89ff 100644 --- a/src/vs/platform/contextview/browser/contextMenuHandler.ts +++ b/src/vs/platform/contextview/browser/contextMenuHandler.ts @@ -123,9 +123,7 @@ export class ContextMenuHandler { }, focus: () => { - if (menu) { - menu.focus(!!delegate.autoSelectFirstItem); - } + menu?.focus(!!delegate.autoSelectFirstItem); }, onHide: (didCancel?: boolean) => { diff --git a/src/vs/platform/externalServices/common/serviceMachineId.ts b/src/vs/platform/externalServices/common/serviceMachineId.ts index b6422b8b25a..6767e7d7225 100644 --- a/src/vs/platform/externalServices/common/serviceMachineId.ts +++ b/src/vs/platform/externalServices/common/serviceMachineId.ts @@ -30,8 +30,8 @@ export async function getServiceMachineId(environmentService: IEnvironmentServic //noop } } - if (storageService) { - storageService.store('storage.serviceMachineId', uuid, StorageScope.APPLICATION, StorageTarget.MACHINE); - } + + storageService?.store('storage.serviceMachineId', uuid, StorageScope.APPLICATION, StorageTarget.MACHINE); + return uuid; } diff --git a/src/vs/platform/files/browser/indexedDBFileSystemProvider.ts b/src/vs/platform/files/browser/indexedDBFileSystemProvider.ts index dfb0eda9b1f..36b2f65cdfc 100644 --- a/src/vs/platform/files/browser/indexedDBFileSystemProvider.ts +++ b/src/vs/platform/files/browser/indexedDBFileSystemProvider.ts @@ -459,9 +459,7 @@ export class IndexedDBFileSystemProvider extends Disposable implements IFileSyst if (changes.length) { this._onDidChangeFile.fire(changes); - if (this.changesBroadcastChannel) { - this.changesBroadcastChannel.postChanges(changes); - } + this.changesBroadcastChannel?.postChanges(changes); } } diff --git a/src/vs/platform/log/common/bufferLog.ts b/src/vs/platform/log/common/bufferLog.ts index df1bdc72c0c..58b9e41e60c 100644 --- a/src/vs/platform/log/common/bufferLog.ts +++ b/src/vs/platform/log/common/bufferLog.ts @@ -32,9 +32,7 @@ export class BufferLogService extends AbstractLogger implements ILogService { super(); this.setLevel(logLevel); this._register(this.onDidChangeLogLevel(level => { - if (this._logger) { - this._logger.setLevel(level); - } + this._logger?.setLevel(level); })); } diff --git a/src/vs/platform/log/node/spdlogLog.ts b/src/vs/platform/log/node/spdlogLog.ts index e72d4db4a36..af7a576df1c 100644 --- a/src/vs/platform/log/node/spdlogLog.ts +++ b/src/vs/platform/log/node/spdlogLog.ts @@ -57,9 +57,7 @@ export class SpdLogLogger extends AbstractMessageLogger implements ILogger { this.setLevel(level); this._loggerCreationPromise = this._createSpdLogLogger(name, filepath, rotating, donotUseFormatters); this._register(this.onDidChangeLogLevel(level => { - if (this._logger) { - this._logger.setLevel(level); - } + this._logger?.setLevel(level); })); } diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index f7bd477150d..f9fdc695940 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -228,9 +228,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } const window = this.windowById(windowId); - if (window) { - window.focus({ force: options?.force ?? false }); - } + window?.focus({ force: options?.force ?? false }); } async setMinimumSize(windowId: number | undefined, width: number | undefined, height: number | undefined): Promise { @@ -436,16 +434,12 @@ export class NativeHostMainService extends Disposable implements INativeHostMain async setRepresentedFilename(windowId: number | undefined, path: string): Promise { const window = this.windowById(windowId); - if (window) { - window.setRepresentedFilename(path); - } + window?.setRepresentedFilename(path); } async setDocumentEdited(windowId: number | undefined, edited: boolean): Promise { const window = this.windowById(windowId); - if (window) { - window.setDocumentEdited(edited); - } + window?.setDocumentEdited(edited); } async openExternal(windowId: number | undefined, url: string): Promise { @@ -657,9 +651,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain async updateTouchBar(windowId: number | undefined, items: ISerializableCommandAction[][]): Promise { const window = this.windowById(windowId); - if (window) { - window.updateTouchBar(items); - } + window?.updateTouchBar(items); } //#endregion diff --git a/src/vs/platform/quickinput/browser/quickInput.ts b/src/vs/platform/quickinput/browser/quickInput.ts index a8f94e4f107..e235c7aeb9a 100644 --- a/src/vs/platform/quickinput/browser/quickInput.ts +++ b/src/vs/platform/quickinput/browser/quickInput.ts @@ -113,9 +113,7 @@ export class QuickInputService extends Themable implements IQuickInputService { this.resetContextKeys(); - if (key) { - key.set(true); - } + key?.set(true); } private resetContextKeys() { diff --git a/src/vs/platform/storage/electron-main/storageIpc.ts b/src/vs/platform/storage/electron-main/storageIpc.ts index b0473d0d53d..5cfb0aa2d96 100644 --- a/src/vs/platform/storage/electron-main/storageIpc.ts +++ b/src/vs/platform/storage/electron-main/storageIpc.ts @@ -120,9 +120,7 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel } } - if (items.delete) { - items.delete.forEach(key => storage.delete(key)); - } + items.delete?.forEach(key => storage.delete(key)); break; } diff --git a/src/vs/platform/telemetry/browser/errorTelemetry.ts b/src/vs/platform/telemetry/browser/errorTelemetry.ts index f539d9ca69d..ae2cea2fe2e 100644 --- a/src/vs/platform/telemetry/browser/errorTelemetry.ts +++ b/src/vs/platform/telemetry/browser/errorTelemetry.ts @@ -17,9 +17,7 @@ export default class ErrorTelemetry extends BaseErrorTelemetry { } globals.onerror = function (message: string, filename: string, line: number, column?: number, e?: any) { that._onUncaughtError(message, filename, line, column, e); - if (oldOnError) { - oldOnError.apply(this, arguments); - } + oldOnError?.apply(this, arguments); }; this._disposables.add(toDisposable(() => { if (oldOnError) { diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index 3e48db34a63..0fd58fb66bc 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -231,9 +231,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati case 'Cwd': { this._createOrGetCwdDetection().updateCwd(value); const commandDetection = this.capabilities.get(TerminalCapability.CommandDetection); - if (commandDetection) { - commandDetection.setCwd(value); - } + commandDetection?.setCwd(value); return true; } case 'IsWindows': { diff --git a/src/vs/platform/userDataSync/common/extensionsMerge.ts b/src/vs/platform/userDataSync/common/extensionsMerge.ts index 8236bb281b3..8def92906ef 100644 --- a/src/vs/platform/userDataSync/common/extensionsMerge.ts +++ b/src/vs/platform/userDataSync/common/extensionsMerge.ts @@ -45,9 +45,7 @@ export function merge(localExtensions: ISyncExtensionWithVersion[], remoteExtens const addUUID = (identifier: IExtensionIdentifier) => { if (identifier.uuid) { uuids.set(identifier.id.toLowerCase(), identifier.uuid); } }; localExtensions.forEach(({ identifier }) => addUUID(identifier)); remoteExtensions.forEach(({ identifier }) => addUUID(identifier)); - if (lastSyncExtensions) { - lastSyncExtensions.forEach(({ identifier }) => addUUID(identifier)); - } + lastSyncExtensions?.forEach(({ identifier }) => addUUID(identifier)); const getKey = (extension: ISyncExtension): string => { const uuid = extension.identifier.uuid || uuids.get(extension.identifier.id.toLowerCase()); diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 4f7156d8d33..92cba61a462 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -1357,9 +1357,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // Add as window tab if configured (macOS only) if (options.forceNewTabbedWindow) { const activeWindow = this.getLastActiveWindow(); - if (activeWindow) { - activeWindow.addTabbedWindow(createdWindow); - } + activeWindow?.addTabbedWindow(createdWindow); } // Add to our list of windows @@ -1475,9 +1473,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic sendToFocused(channel: string, ...args: any[]): void { const focusedWindow = this.getFocusedWindow() || this.getLastActiveWindow(); - if (focusedWindow) { - focusedWindow.sendWhenReady(channel, CancellationToken.None, ...args); - } + focusedWindow?.sendWhenReady(channel, CancellationToken.None, ...args); } sendToAll(channel: string, payload?: any, windowIdsToIgnore?: number[]): void { diff --git a/src/vs/server/node/extensionHostConnection.ts b/src/vs/server/node/extensionHostConnection.ts index e5c745648ec..9e0a646f225 100644 --- a/src/vs/server/node/extensionHostConnection.ts +++ b/src/vs/server/node/extensionHostConnection.ts @@ -326,9 +326,7 @@ export class ExtensionHostConnection { const namedPipeServer = net.createServer(); namedPipeServer.on('error', reject); namedPipeServer.listen(pipeName, () => { - if (namedPipeServer) { - namedPipeServer.removeListener('error', reject); - } + namedPipeServer?.removeListener('error', reject); resolve({ pipeName, namedPipeServer }); }); }); diff --git a/src/vs/workbench/api/browser/mainThreadDebugService.ts b/src/vs/workbench/api/browser/mainThreadDebugService.ts index 6d2b31c86cb..c0ec9d1b550 100644 --- a/src/vs/workbench/api/browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/browser/mainThreadDebugService.ts @@ -242,9 +242,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb public $setDebugSessionName(sessionId: DebugSessionUUID, name: string): void { const session = this.debugService.getModel().getSession(sessionId); - if (session) { - session.setName(name); - } + session?.setName(name); } public $customDebugAdapterRequest(sessionId: DebugSessionUUID, request: string, args: any): Promise { @@ -284,9 +282,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb public $appendDebugConsole(value: string): void { // Use warning as severity to get the orange color for messages coming from the debug extension const session = this.debugService.getViewModel().focusedSession; - if (session) { - session.appendToRepl(value, severity.Warning); - } + session?.appendToRepl(value, severity.Warning); } public $acceptDAMessage(handle: number, message: DebugProtocol.ProtocolMessage) { diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 1e4a6f043d3..bf5de3a19a2 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -140,9 +140,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread } private static _reviveCodeActionDto(data: ReadonlyArray): languages.CodeAction[] { - if (data) { - data.forEach(code => reviveWorkspaceEditDto(code.edit)); - } + data?.forEach(code => reviveWorkspaceEditDto(code.edit)); return data; } diff --git a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts index b93132cf613..4ebdadf9976 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts @@ -266,9 +266,7 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape const updates = data.value; try { const execution = this._executions.get(handle); - if (execution) { - execution.update(updates.map(NotebookDto.fromCellExecuteUpdateDto)); - } + execution?.update(updates.map(NotebookDto.fromCellExecuteUpdateDto)); } catch (e) { onUnexpectedError(e); } @@ -277,9 +275,7 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape $completeExecution(handle: number, data: SerializableObjectWithBuffers): void { try { const execution = this._executions.get(handle); - if (execution) { - execution.complete(NotebookDto.fromCellExecuteCompleteDto(data.value)); - } + execution?.complete(NotebookDto.fromCellExecuteCompleteDto(data.value)); } catch (e) { onUnexpectedError(e); } finally { diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index db958829ce8..b280f677abe 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -318,9 +318,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape public $sendProcessProperty(terminalId: number, property: IProcessProperty): void { if (property.type === ProcessPropertyType.Title) { const instance = this._terminalService.getInstanceFromId(terminalId); - if (instance) { - instance.refreshTabLabels(property.value, TitleEventSource.Api); - } + instance?.refreshTabLabels(property.value, TitleEventSource.Api); } this._terminalProcessProxies.get(terminalId)?.emitProcessProperty(property); } diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts index 178ab59c5ef..9e0e03f1379 100644 --- a/src/vs/workbench/api/common/extHostDebugService.ts +++ b/src/vs/workbench/api/common/extHostDebugService.ts @@ -495,9 +495,7 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E } const da = this._debugAdapters.get(debugAdapterHandle); - if (da) { - da.sendMessage(message); - } + da?.sendMessage(message); } public $stopDASession(debugAdapterHandle: number): Promise { @@ -663,9 +661,7 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E public async $acceptDebugSessionNameChanged(sessionDto: IDebugSessionDto, name: string): Promise { const session = await this.getSession(sessionDto); - if (session) { - session._acceptNameChanged(name); - } + session?._acceptNameChanged(name); } public async $acceptDebugSessionCustomEvent(sessionDto: IDebugSessionDto, event: any): Promise { diff --git a/src/vs/workbench/api/common/extHostDiagnostics.ts b/src/vs/workbench/api/common/extHostDiagnostics.ts index 9f5383dcd59..3eb7aeb41cc 100644 --- a/src/vs/workbench/api/common/extHostDiagnostics.ts +++ b/src/vs/workbench/api/common/extHostDiagnostics.ts @@ -108,9 +108,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { } } else { const currentDiagnostics = this.#data.get(uri); - if (currentDiagnostics) { - currentDiagnostics.push(...diagnostics); - } + currentDiagnostics?.push(...diagnostics); } } } diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 7608468e560..afdb6fbb015 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -88,9 +88,7 @@ class DocumentSymbolAdapter { } const parent = parentStack[parentStack.length - 1]; if (EditorRange.containsRange(parent.range, element.range) && !EditorRange.equalsRange(parent.range, element.range)) { - if (parent.children) { - parent.children.push(element); - } + parent.children?.push(element); parentStack.push(element); break; } diff --git a/src/vs/workbench/api/common/extHostQuickOpen.ts b/src/vs/workbench/api/common/extHostQuickOpen.ts index 67c56f94474..1bc50dd99e2 100644 --- a/src/vs/workbench/api/common/extHostQuickOpen.ts +++ b/src/vs/workbench/api/common/extHostQuickOpen.ts @@ -218,9 +218,7 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx $onDidChangeValue(sessionId: number, value: string): void { const session = this._sessions.get(sessionId); - if (session) { - session._fireDidChangeValue(value); - } + session?._fireDidChangeValue(value); } $onDidAccept(sessionId: number): void { @@ -246,9 +244,7 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx $onDidTriggerButton(sessionId: number, handle: number): void { const session = this._sessions.get(sessionId); - if (session) { - session._fireDidTriggerButton(handle); - } + session?._fireDidTriggerButton(handle); } $onDidTriggerItemButton(sessionId: number, itemHandle: number, buttonHandle: number): void { diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 1aa1e678e00..9a898fcfe4a 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -538,9 +538,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I public async $acceptTerminalProcessId(id: number, processId: number): Promise { const terminal = this._getTerminalById(id); - if (terminal) { - terminal._setProcessId(processId); - } + terminal?._setProcessId(processId); } public async $startExtensionTerminal(id: number, initialDimensions: ITerminalDimensionsDto | undefined): Promise { @@ -693,9 +691,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I this._terminalLinkCache.delete(terminalId); const oldToken = this._terminalLinkCancellationSource.get(terminalId); - if (oldToken) { - oldToken.dispose(true); - } + oldToken?.dispose(true); const cancellationSource = new CancellationTokenSource(); this._terminalLinkCancellationSource.set(terminalId, cancellationSource); diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 744177bb76c..b4dfa9ec1de 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1210,9 +1210,7 @@ export class DocumentSymbol { if (!candidate.range.contains(candidate.selectionRange)) { throw new Error('selectionRange must be contained in fullRange'); } - if (candidate.children) { - candidate.children.forEach(DocumentSymbol.validate); - } + candidate.children?.forEach(DocumentSymbol.validate); } name: string; diff --git a/src/vs/workbench/api/node/extHostSearch.ts b/src/vs/workbench/api/node/extHostSearch.ts index 9867e2a5e6b..8e1dab9ef1b 100644 --- a/src/vs/workbench/api/node/extHostSearch.ts +++ b/src/vs/workbench/api/node/extHostSearch.ts @@ -103,9 +103,7 @@ export class NativeExtHostSearch extends ExtHostSearch { } override $clearCache(cacheKey: string): Promise { - if (this._internalFileSearchProvider) { - this._internalFileSearchProvider.clearCache(cacheKey); - } + this._internalFileSearchProvider?.clearCache(cacheKey); return super.$clearCache(cacheKey); } diff --git a/src/vs/workbench/api/node/extHostStoragePaths.ts b/src/vs/workbench/api/node/extHostStoragePaths.ts index 06cfede9d13..8348d9490b2 100644 --- a/src/vs/workbench/api/node/extHostStoragePaths.ts +++ b/src/vs/workbench/api/node/extHostStoragePaths.ts @@ -63,9 +63,7 @@ export class ExtensionStoragePaths extends CommonExtensionStoragePaths { override onWillDeactivateAll(): void { // the lock will be released soon - if (this._workspaceStorageLock) { - this._workspaceStorageLock.setWillRelease(6000); - } + this._workspaceStorageLock?.setWillRelease(6000); } } diff --git a/src/vs/workbench/browser/actions/developerActions.ts b/src/vs/workbench/browser/actions/developerActions.ts index c5a04846dc0..b6967769f05 100644 --- a/src/vs/workbench/browser/actions/developerActions.ts +++ b/src/vs/workbench/browser/actions/developerActions.ts @@ -48,9 +48,7 @@ class InspectContextKeysAction extends Action2 { const stylesheet = createStyleSheet(); disposables.add(toDisposable(() => { - if (stylesheet.parentNode) { - stylesheet.parentNode.removeChild(stylesheet); - } + stylesheet.parentNode?.removeChild(stylesheet); })); createCSSRule('*', 'cursor: crosshair !important;', stylesheet); diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index e81c92e69b1..c6e8fae126b 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -495,9 +495,7 @@ export class ActivitybarPart extends Part implements IPaneCompositeSelectorPart this.keyboardNavigationDisposables.add(addDisposableListener(this.compositeBarContainer, EventType.KEY_DOWN, e => { const kbEvent = new StandardKeyboardEvent(e); if (kbEvent.equals(KeyCode.DownArrow) || kbEvent.equals(KeyCode.RightArrow)) { - if (this.globalActivityActionBar) { - this.globalActivityActionBar.focus(true); - } + this.globalActivityActionBar?.focus(true); } else if (kbEvent.equals(KeyCode.UpArrow) || kbEvent.equals(KeyCode.LeftArrow)) { if (this.menuBar) { this.menuBar.toggleFocus(); @@ -511,9 +509,7 @@ export class ActivitybarPart extends Part implements IPaneCompositeSelectorPart this.keyboardNavigationDisposables.add(addDisposableListener(this.globalActivitiesContainer, EventType.KEY_DOWN, e => { const kbEvent = new StandardKeyboardEvent(e); if (kbEvent.equals(KeyCode.UpArrow) || kbEvent.equals(KeyCode.LeftArrow)) { - if (this.compositeBar) { - this.compositeBar.focus(this.getVisiblePaneCompositeIds().length - 1); - } + this.compositeBar?.focus(this.getVisiblePaneCompositeIds().length - 1); } })); } diff --git a/src/vs/workbench/browser/parts/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts index 9d960b0bf5c..d15c1f36d96 100644 --- a/src/vs/workbench/browser/parts/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -79,9 +79,7 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop { } this.openComposite(newContainer.id, true).then(composite => { - if (composite) { - composite.openView(viewToMove.id, true); - } + composite?.openView(viewToMove.id, true); }); } } @@ -301,9 +299,7 @@ export class CompositeBar extends Widget implements ICompositeBar { } focus(index?: number): void { - if (this.compositeSwitcherBar) { - this.compositeSwitcherBar.focus(index); - } + this.compositeSwitcherBar?.focus(index); } recomputeSizes(): void { diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index b6fddb4625f..9ad13c8a659 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -232,9 +232,7 @@ export abstract class CompositePart extends Part { // Take Composite on-DOM and show const contentArea = this.getContentArea(); - if (contentArea) { - contentArea.appendChild(compositeContainer); - } + contentArea?.appendChild(compositeContainer); show(compositeContainer); // Setup action runner @@ -362,9 +360,7 @@ export abstract class CompositePart extends Part { } // Clear any running Progress - if (this.progressBar) { - this.progressBar.stop().hide(); - } + this.progressBar?.stop().hide(); // Empty Actions if (this.toolBar) { @@ -477,9 +473,7 @@ export abstract class CompositePart extends Part { this.contentAreaSize = Dimension.lift(super.layoutContents(width, height).contentSize); // Layout composite - if (this.activeComposite) { - this.activeComposite.layout(this.contentAreaSize); - } + this.activeComposite?.layout(this.contentAreaSize); } protected removeComposite(compositeId: string): boolean { diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index a07f202b076..5dae9e8d098 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -1281,9 +1281,7 @@ function registerOtherEditorCommands(): void { const editorGroupService = accessor.get(IEditorGroupsService); const { group } = resolveCommandsContext(editorGroupService, getCommandsContext(resourceOrContext, context)); - if (group) { - group.lock(locked ?? !group.isLocked); - } + group?.lock(locked ?? !group.isLocked); } registerAction2(class extends Action2 { diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 6c5f32362ac..6bc1a580226 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -72,9 +72,7 @@ class GridWidgetView implements IView { } layout(width: number, height: number, top: number, left: number): void { - if (this.gridWidget) { - this.gridWidget.layout(width, height, top, left); - } + this.gridWidget?.layout(width, height, top, left); } dispose(): void { @@ -595,9 +593,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro this.doUpdateMostRecentActive(group, true); // Mark previous one as inactive - if (previousActiveGroup) { - previousActiveGroup.setActive(false); - } + previousActiveGroup?.setActive(false); // Mark group as new active group.setActive(true); diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 1f2594bc9f0..9fbf579fa27 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -731,9 +731,7 @@ export abstract class BasePanelPart extends CompositePart impleme const primaryActions = this.globalActions.getPrimaryActions(); const secondaryActions = this.globalActions.getSecondaryActions(); - if (this.globalToolBar) { - this.globalToolBar.setActions(prepareActions(primaryActions), prepareActions(secondaryActions)); - } + this.globalToolBar?.setActions(prepareActions(primaryActions), prepareActions(secondaryActions)); } private getCompositeActions(compositeId: string): { activityAction: PanelActivityAction; pinnedAction: ToggleCompositePinnedAction } { diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index a22ba7b284a..464c1e59ef6 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -751,9 +751,7 @@ export class CustomMenubarControl extends MenubarControl { if (!this.focusInsideMenubar) { const actions: IAction[] = []; updateActions(menu, actions, title); - if (this.menubar) { - this.menubar.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) }); - } + this.menubar?.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) }); } })); @@ -763,9 +761,7 @@ export class CustomMenubarControl extends MenubarControl { if (!this.focusInsideMenubar) { const actions: IAction[] = []; updateActions(menu, actions, title); - if (this.menubar) { - this.menubar.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) }); - } + this.menubar?.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) }); } })); } diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 3d4dcc3eab6..32078d1bf2b 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -722,9 +722,7 @@ abstract class AbstractTreeView extends Disposable implements ITreeView { this._width = width; const treeHeight = height - DOM.getTotalHeight(this.messageElement); this.treeContainer.style.height = treeHeight + 'px'; - if (this.tree) { - this.tree.layout(treeHeight, width); - } + this.tree?.layout(treeHeight, width); } } @@ -780,9 +778,7 @@ abstract class AbstractTreeView extends Disposable implements ITreeView { } setSelection(items: ITreeItem[]): void { - if (this.tree) { - this.tree.setSelection(items); - } + this.tree?.setSelection(items); } setFocus(item: ITreeItem): void { diff --git a/src/vs/workbench/common/editor/textResourceEditorInput.ts b/src/vs/workbench/common/editor/textResourceEditorInput.ts index 94236604209..ee14358d2b8 100644 --- a/src/vs/workbench/common/editor/textResourceEditorInput.ts +++ b/src/vs/workbench/common/editor/textResourceEditorInput.ts @@ -133,9 +133,7 @@ export class TextResourceEditorInput extends AbstractTextResourceEditorInput imp setLanguageId(languageId: string): void { this.setPreferredLanguageId(languageId); - if (this.cachedModel) { - this.cachedModel.setLanguageId(languageId); - } + this.cachedModel?.setLanguageId(languageId); } setPreferredLanguageId(languageId: string): void { diff --git a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts index 2e100dc8f09..9041f5e39de 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts @@ -204,9 +204,7 @@ export class TrimFinalNewLinesParticipant implements ITextFileSaveParticipant { model.pushEditOperations(prevSelection, [EditOperation.delete(deletionRange)], _edits => prevSelection); - if (editor) { - editor.setSelections(prevSelection); - } + editor?.setSelections(prevSelection); } } diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index 7babdf9a73d..e37c1bfd5fe 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -361,9 +361,7 @@ export class CommentNode extends Disposable { } }, reaction.iconPath, reaction.count); - if (this._reactionsActionBar) { - this._reactionsActionBar.push(action, { label: true, icon: true }); - } + this._reactionsActionBar?.push(action, { label: true, icon: true }); }); if (hasReactionHandler) { @@ -468,9 +466,7 @@ export class CommentNode extends Disposable { this._register(menu); this._register(menu.onDidChange(() => { - if (this._commentFormActions) { - this._commentFormActions.setActions(menu); - } + this._commentFormActions?.setActions(menu); })); this._commentFormActions = new CommentFormActions(formActions, (action: IAction): void => { diff --git a/src/vs/workbench/contrib/comments/browser/commentService.ts b/src/vs/workbench/contrib/comments/browser/commentService.ts index abddde8f53c..099e848c97b 100644 --- a/src/vs/workbench/contrib/comments/browser/commentService.ts +++ b/src/vs/workbench/contrib/comments/browser/commentService.ts @@ -206,9 +206,7 @@ export class CommentService extends Disposable implements ICommentService { disposeCommentThread(owner: string, threadId: string) { const controller = this.getCommentController(owner); - if (controller) { - controller.deleteCommentThreadMain(threadId); - } + controller?.deleteCommentThreadMain(threadId); } getCommentMenus(owner: string): CommentMenus { diff --git a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts index 54af2c8fa32..9d3eb7de6bf 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts @@ -858,9 +858,7 @@ export class CommentController implements IEditorContribution { } public closeWidget(): void { - if (this._commentWidgets) { - this._commentWidgets.forEach(widget => widget.hide()); - } + this._commentWidgets?.forEach(widget => widget.hide()); this.editor.focus(); this.editor.revealRangeInCenter(this.editor.getSelection()!); diff --git a/src/vs/workbench/contrib/configExporter/electron-sandbox/configurationExportHelper.ts b/src/vs/workbench/contrib/configExporter/electron-sandbox/configurationExportHelper.ts index 83a37ddc12e..4aed7b2d974 100644 --- a/src/vs/workbench/contrib/configExporter/electron-sandbox/configurationExportHelper.ts +++ b/src/vs/workbench/contrib/configExporter/electron-sandbox/configurationExportHelper.ts @@ -98,9 +98,7 @@ export class DefaultConfigurationExportHelper { } } - if (config.allOf) { - config.allOf.forEach(processConfig); - } + config.allOf?.forEach(processConfig); }; configurations.forEach(processConfig); diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index 2e020ea8fa6..8772e62d4f9 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -242,9 +242,7 @@ export class BreakpointsView extends ViewPane { } super.layoutBody(height, width); - if (this.list) { - this.list.layout(height, width); - } + this.list?.layout(height, width); try { this.ignoreLayout = true; this.updateSize(); diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 3ff60d94d24..4a53a4e0c93 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -925,9 +925,7 @@ export class DebugSession implements IDebugSession { } catch (e) { // Disconnect the debug session on configuration done error #10596 this.notificationService.error(e); - if (this.raw) { - this.raw.disconnect({}); - } + this.raw?.disconnect({}); } } @@ -1030,9 +1028,7 @@ export class DebugSession implements IDebugSession { this.stoppedDetails = this.stoppedDetails.filter(sd => sd.threadId !== threadId); const tokens = this.cancellationMap.get(threadId); this.cancellationMap.delete(threadId); - if (tokens) { - tokens.forEach(t => t.cancel()); - } + tokens?.forEach(t => t.cancel()); } else { this.stoppedDetails = []; this.cancelAllRequests(); diff --git a/src/vs/workbench/contrib/debug/browser/debugStatus.ts b/src/vs/workbench/contrib/debug/browser/debugStatus.ts index 898b0329b5e..c4a8ef6be74 100644 --- a/src/vs/workbench/contrib/debug/browser/debugStatus.ts +++ b/src/vs/workbench/contrib/debug/browser/debugStatus.ts @@ -49,9 +49,7 @@ export class DebugStatusContribution implements IWorkbenchContribution { } })); this.toDispose.push(this.debugService.getConfigurationManager().onDidSelectConfiguration(e => { - if (this.entryAccessor) { - this.entryAccessor.update(this.entry); - } + this.entryAccessor?.update(this.entry); })); } diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 682133ef7bd..2bfc78a80ae 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -284,9 +284,7 @@ export class DisassemblyView extends EditorPane { } layout(dimension: Dimension): void { - if (this._disassembledInstructions) { - this._disassembledInstructions.layout(dimension.height); - } + this._disassembledInstructions?.layout(dimension.height); } /** diff --git a/src/vs/workbench/contrib/debug/common/debugContentProvider.ts b/src/vs/workbench/contrib/debug/common/debugContentProvider.ts index 772193c7f16..5cdb5c988cb 100644 --- a/src/vs/workbench/contrib/debug/common/debugContentProvider.ts +++ b/src/vs/workbench/contrib/debug/common/debugContentProvider.ts @@ -62,9 +62,7 @@ export class DebugContentProvider implements IWorkbenchContribution, ITextModelC * If there is no model for the given resource, this method does nothing. */ static refreshDebugContent(resource: uri): void { - if (DebugContentProvider.INSTANCE) { - DebugContentProvider.INSTANCE.createOrUpdateContentModel(resource, false); - } + DebugContentProvider.INSTANCE?.createOrUpdateContentModel(resource, false); } /** diff --git a/src/vs/workbench/contrib/debug/node/debugAdapter.ts b/src/vs/workbench/contrib/debug/node/debugAdapter.ts index 4f0bf897787..b4f6c7e4daf 100644 --- a/src/vs/workbench/contrib/debug/node/debugAdapter.ts +++ b/src/vs/workbench/contrib/debug/node/debugAdapter.ts @@ -259,9 +259,7 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { // }); this.serverProcess.stderr!.on('data', (data: string) => { const channel = outputService.getChannel(ExtensionsChannelId); - if (channel) { - channel.append(sanitize(data)); - } + channel?.append(sanitize(data)); }); } else { this.serverProcess.stderr!.resume(); diff --git a/src/vs/workbench/contrib/experiments/browser/experimentalPrompt.ts b/src/vs/workbench/contrib/experiments/browser/experimentalPrompt.ts index a850ee85522..a10722cff13 100644 --- a/src/vs/workbench/contrib/experiments/browser/experimentalPrompt.ts +++ b/src/vs/workbench/contrib/experiments/browser/experimentalPrompt.ts @@ -58,9 +58,7 @@ export class ExperimentalPrompts extends Disposable implements IWorkbenchContrib this.paneCompositeService.openPaneComposite(EXTENSIONS_VIEWLET_ID, ViewContainerLocation.Sidebar, true) .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { - if (viewlet) { - viewlet.search('curated:' + command.curatedExtensionsKey); - } + viewlet?.search('curated:' + command.curatedExtensionsKey); }); } else if (command.codeCommand) { this.commandService.executeCommand(command.codeCommand.id, ...command.codeCommand.arguments); diff --git a/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts index 302bdc38202..c801cfe94f6 100644 --- a/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts @@ -95,9 +95,7 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane { protected async _updateExtensions(): Promise { this._elements = await this._resolveExtensions(); - if (this._list) { - this._list.splice(0, this._list.length, this._elements); - } + this._list?.splice(0, this._list.length, this._elements); } private async _resolveExtensions(): Promise { @@ -475,9 +473,7 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane { } public layout(dimension: Dimension): void { - if (this._list) { - this._list.layout(dimension.height); - } + this._list?.layout(dimension.height); } protected abstract _getProfileInfo(): IExtensionHostProfile | null; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 5667fe59720..13058dbd848 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -1757,9 +1757,7 @@ registerAction2(class StartExtensionEditorFindNextAction extends Action2 { } run(accessor: ServicesAccessor): any { const extensionEditor = getExtensionEditor(accessor); - if (extensionEditor) { - extensionEditor.runFindAction(false); - } + extensionEditor?.runFindAction(false); } }); @@ -1779,9 +1777,7 @@ registerAction2(class StartExtensionEditorFindPreviousAction extends Action2 { } run(accessor: ServicesAccessor): any { const extensionEditor = getExtensionEditor(accessor); - if (extensionEditor) { - extensionEditor.runFindAction(true); - } + extensionEditor?.runFindAction(true); } }); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 03dc8eaca40..710e811a4f7 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -921,9 +921,7 @@ export abstract class ExtensionDropDownAction extends ExtensionAction { } public override run({ actionGroups, disposeActionsOnHide }: { actionGroups: IAction[][]; disposeActionsOnHide: boolean }): Promise { - if (this._actionViewItem) { - this._actionViewItem.showMenu(actionGroups, disposeActionsOnHide); - } + this._actionViewItem?.showMenu(actionGroups, disposeActionsOnHide); return Promise.resolve(); } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index 20d6716ae62..aaf033d7bd5 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -571,9 +571,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE this.root.classList.toggle('narrow', dimension.width <= 250); this.root.classList.toggle('mini', dimension.width <= 200); } - if (this.searchBox) { - this.searchBox.layout(new Dimension(dimension.width - 34 - /*padding*/8, 20)); - } + this.searchBox?.layout(new Dimension(dimension.width - 34 - /*padding*/8, 20)); super.layout(new Dimension(dimension.width, dimension.height - 41)); } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index d2199de3a6e..36ec5fd2d48 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -209,9 +209,7 @@ export class ExtensionsListView extends ViewPane { if (this.bodyTemplate) { this.bodyTemplate.extensionsList.style.height = height + 'px'; } - if (this.list) { - this.list.layout(height, width); - } + this.list?.layout(height, width); } async show(query: string, refresh?: boolean): Promise> { diff --git a/src/vs/workbench/contrib/extensions/electron-sandbox/extensionProfileService.ts b/src/vs/workbench/contrib/extensions/electron-sandbox/extensionProfileService.ts index 44295a6868b..6e07fd90be6 100644 --- a/src/vs/workbench/contrib/extensions/electron-sandbox/extensionProfileService.ts +++ b/src/vs/workbench/contrib/extensions/electron-sandbox/extensionProfileService.ts @@ -92,9 +92,7 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio const timeStarted = Date.now(); const handle = setInterval(() => { - if (this.profilingStatusBarIndicator) { - this.profilingStatusBarIndicator.update({ ...indicator, text: nls.localize('profilingExtensionHostTime', "Profiling Extension Host ({0} sec)", Math.round((new Date().getTime() - timeStarted) / 1000)), }); - } + this.profilingStatusBarIndicator?.update({ ...indicator, text: nls.localize('profilingExtensionHostTime', "Profiling Extension Host ({0} sec)", Math.round((new Date().getTime() - timeStarted) / 1000)), }); }, 1000); this.profilingStatusBarIndicatorLabelUpdater.value = toDisposable(() => clearInterval(handle)); diff --git a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts index f74f5df3aa4..6188a5bcb86 100644 --- a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts @@ -224,9 +224,7 @@ export class ExplorerViewPaneContainer extends ViewPaneContainer { } const openEditorsView = this.getOpenEditorsView(); - if (openEditorsView) { - openEditorsView.setStructuralRefreshDelay(0); - } + openEditorsView?.setStructuralRefreshDelay(0); } }); } diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index 9f61cd64ee5..1e2e605ec5e 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -489,9 +489,7 @@ export class ExplorerView extends ViewPane implements IExplorerView { const element = e.node.element?.element; if (element) { const navigationController = this.renderer.getCompressedNavigationController(element instanceof Array ? element[0] : element); - if (navigationController) { - navigationController.updateCollapsed(e.node.collapsed); - } + navigationController?.updateCollapsed(e.node.collapsed); } })); @@ -765,9 +763,7 @@ export class ExplorerView extends ViewPane implements IExplorerView { itemsCopied(stats: ExplorerItem[], cut: boolean, previousCut: ExplorerItem[] | undefined): void { this.fileCopiedContextKey.set(stats.length > 0); this.resourceCutContextKey.set(cut && stats.length > 0); - if (previousCut) { - previousCut.forEach(item => this.tree.rerender(item)); - } + previousCut?.forEach(item => this.tree.rerender(item)); if (cut) { stats.forEach(s => this.tree.rerender(s)); } diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index e3e79b236a0..ebb2b295f0f 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -320,9 +320,7 @@ export class OpenEditorsView extends ViewPane { protected override layoutBody(height: number, width: number): void { super.layoutBody(height, width); - if (this.list) { - this.list.layout(height, width); - } + this.list?.layout(height, width); } private get showGroups(): boolean { diff --git a/src/vs/workbench/contrib/files/common/explorerModel.ts b/src/vs/workbench/contrib/files/common/explorerModel.ts index ac4207fb18d..61d0ab50ca7 100644 --- a/src/vs/workbench/contrib/files/common/explorerModel.ts +++ b/src/vs/workbench/contrib/files/common/explorerModel.ts @@ -178,13 +178,9 @@ export class ExplorerItem { private updateName(value: string): void { // Re-add to parent since the parent has a name map to children and the name might have changed - if (this._parent) { - this._parent.removeChild(this); - } + this._parent?.removeChild(this); this._name = value; - if (this._parent) { - this._parent.addChild(this); - } + this._parent?.addChild(this); } getId(): string { @@ -411,12 +407,8 @@ export class ExplorerItem { * Moves this element under a new parent element. */ move(newParent: ExplorerItem): void { - if (this.nestedParent) { - this.nestedParent.removeChild(this); - } - if (this._parent) { - this._parent.removeChild(this); - } + this.nestedParent?.removeChild(this); + this._parent?.removeChild(this); newParent.removeChild(this); // make sure to remove any previous version of the file if any newParent.addChild(this); this.updateResource(true); diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index 7223d0cdc20..e363e609335 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -267,9 +267,7 @@ class ToggleMultilineActionViewItem extends ActionViewItem { } private updateExpandedAttribute(): void { - if (this.element) { - this.element.setAttribute('aria-expanded', `${this._action.class === ThemeIcon.asClassName(expandedIcon)}`); - } + this.element?.setAttribute('aria-expanded', `${this._action.class === ThemeIcon.asClassName(expandedIcon)}`); } } diff --git a/src/vs/workbench/contrib/markers/browser/markersView.ts b/src/vs/workbench/contrib/markers/browser/markersView.ts index 18fa3e2930f..bf75c6804ca 100644 --- a/src/vs/workbench/contrib/markers/browser/markersView.ts +++ b/src/vs/workbench/contrib/markers/browser/markersView.ts @@ -227,9 +227,7 @@ export class MarkersView extends ViewPane implements IMarkersView { const wasSmallLayout = this.smallLayout; this.smallLayout = width < 600 && height > 100; if (this.smallLayout !== wasSmallLayout) { - if (this.filterActionBar) { - this.filterActionBar.getContainer().classList.toggle('hide', !this.smallLayout); - } + this.filterActionBar?.getContainer().classList.toggle('hide', !this.smallLayout); } const contentHeight = this.smallLayout ? height - 44 : height; if (this.messageBoxContainer) { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index 1ae1e7ea6bf..367c1f80e41 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -97,9 +97,7 @@ export class NotebookEditor extends EditorPane implements IEditorPaneWithSelecti } private _updateReadonly(input: NotebookEditorInput): void { - if (this._widget.value) { - this._widget.value.setOptions({ isReadOnly: input.hasCapability(EditorInputCapabilities.Readonly) }); - } + this._widget.value?.setOptions({ isReadOnly: input.hasCapability(EditorInputCapabilities.Readonly) }); } get textModel(): NotebookTextModel | undefined { diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/markdownCell.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/markdownCell.ts index 67cdfaf7002..870d4b2d719 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/markdownCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/markdownCell.ts @@ -435,9 +435,7 @@ export class StatefulMarkdownCell extends Disposable { updateEditorOptions(newValue: IEditorOptions): void { this.editorOptions = newValue; - if (this.editor) { - this.editor.updateOptions(this.editorOptions); - } + this.editor?.updateOptions(this.editorOptions); } private layoutFoldingIndicator() { diff --git a/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts b/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts index 51b61d425b1..f07e2ac2bec 100644 --- a/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts +++ b/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts @@ -199,9 +199,7 @@ export class NotebookEditorSimpleWorker implements IRequestHandler, IDisposable public acceptModelChanged(strURL: string, event: NotebookCellsChangedEventDto) { const model = this._models[strURL]; - if (model) { - model.acceptModelChanged(event); - } + model?.acceptModelChanged(event); } public acceptRemovedModel(strURL: string): void { diff --git a/src/vs/workbench/contrib/output/browser/outputServices.ts b/src/vs/workbench/contrib/output/browser/outputServices.ts index f3b78f84cca..c13ec6b5e0c 100644 --- a/src/vs/workbench/contrib/output/browser/outputServices.ts +++ b/src/vs/workbench/contrib/output/browser/outputServices.ts @@ -145,9 +145,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo this.setActiveChannel(channel); this._onActiveOutputChannel.fire(channelId); const outputView = this.viewsService.getActiveViewWithId(OUTPUT_VIEW_ID); - if (outputView) { - outputView.showChannel(channel, true); - } + outputView?.showChannel(channel, true); } } diff --git a/src/vs/workbench/contrib/output/browser/outputView.ts b/src/vs/workbench/contrib/output/browser/outputView.ts index f754c3a78a8..a3b51602a06 100644 --- a/src/vs/workbench/contrib/output/browser/outputView.ts +++ b/src/vs/workbench/contrib/output/browser/outputView.ts @@ -84,9 +84,7 @@ export class OutputViewPane extends ViewPane { override focus(): void { super.focus(); - if (this.editorPromise) { - this.editorPromise.then(() => this.editor.focus()); - } + this.editorPromise?.then(() => this.editor.focus()); } override renderBody(container: HTMLElement): void { diff --git a/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts index 320f1d39ed1..3d1ddf27618 100644 --- a/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts +++ b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts @@ -95,9 +95,7 @@ class PerfModelContentProvider implements ITextModelContentProvider { this._model = this._modelService.getModel(resource) || this._modelService.createModel('Loading...', langId, resource); this._modelDisposables.push(langId.onDidChange(e => { - if (this._model) { - this._model.setMode(e); - } + this._model?.setMode(e); })); this._modelDisposables.push(this._extensionService.onDidChangeExtensionsStatus(this._updateModel, this)); diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts index f74ec72414c..7c4112dca63 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts @@ -506,9 +506,7 @@ export class SearchWidget extends Widget { } override dispose(): void { - if (this.options.focusKey) { - this.options.focusKey.set(false); - } + this.options.focusKey?.set(false); super.dispose(); } } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index dcf4395b126..a13ed3f1843 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -254,9 +254,7 @@ export class SettingsEditor2 extends EditorPane { })); this._register(workspaceTrustManagementService.onDidChangeTrust(() => { - if (this.searchResultModel) { - this.searchResultModel.updateWorkspaceTrust(workspaceTrustManagementService.isWorkspaceTrusted()); - } + this.searchResultModel?.updateWorkspaceTrust(workspaceTrustManagementService.isWorkspaceTrusted()); if (this.settingsTreeModel) { this.settingsTreeModel.updateWorkspaceTrust(workspaceTrustManagementService.isWorkspaceTrusted()); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index e03798c8af3..6b7badf59d3 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -267,9 +267,7 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { this.tags.add(MODIFIED_SETTING_TAG); } - if (this.setting.tags) { - this.setting.tags.forEach(tag => this.tags!.add(tag)); - } + this.setting.tags?.forEach(tag => this.tags!.add(tag)); if (this.setting.restricted) { this.tags.add(REQUIRE_TRUSTED_WORKSPACE_SETTING_TAG); diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index becdd37f551..e6be26f64a1 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -291,9 +291,7 @@ export function cancelSearch(accessor: ServicesAccessor) { export function refreshSearch(accessor: ServicesAccessor) { const viewsService = accessor.get(IViewsService); const searchView = getSearchView(viewsService); - if (searchView) { - searchView.triggerQueryChange({ preserveFocus: false }); - } + searchView?.triggerQueryChange({ preserveFocus: false }); } export function collapseDeepestExpandedLevel(accessor: ServicesAccessor) { diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 17baf536043..e872e6f61eb 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -483,18 +483,14 @@ export class SearchView extends ViewPane { this._register(inputFocusTracker.onDidFocus(() => { this.lastFocusState = 'input'; this.inputBoxFocused.set(true); - if (contextKey) { - contextKey.set(true); - } + contextKey?.set(true); })); this._register(inputFocusTracker.onDidBlur(() => { this.inputBoxFocused.set(this.searchWidget.searchInputHasFocus() || this.searchWidget.replaceInputHasFocus() || this.inputPatternIncludes.inputHasFocus() || this.inputPatternExcludes.inputHasFocus()); - if (contextKey) { - contextKey.set(false); - } + contextKey?.set(false); })); } @@ -752,9 +748,7 @@ export class SearchView extends ViewPane { this._register(Event.debounce(this.tree.onDidOpen, (last, event) => event, 75, true)(options => { if (options.element instanceof Match) { const selectedMatch: Match = options.element; - if (this.currentSelectedFileMatch) { - this.currentSelectedFileMatch.setSelectedMatch(null); - } + this.currentSelectedFileMatch?.setSelectedMatch(null); this.currentSelectedFileMatch = selectedMatch.parent(); this.currentSelectedFileMatch.setSelectedMatch(selectedMatch); diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/common/searchModel.ts index 2fd8f4ab2c8..4df55f37483 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/common/searchModel.ts @@ -528,9 +528,7 @@ export class FolderMatch extends Disposable { bindModel(model: ITextModel): void { const fileMatch = this._fileMatches.get(model.uri); - if (fileMatch) { - fileMatch.bindModel(model); - } + fileMatch?.bindModel(model); } add(raw: IFileMatch[], silent: boolean): void { @@ -784,9 +782,7 @@ export class SearchResult extends Disposable { private onModelAdded(model: ITextModel): void { const folderMatch = this._folderMatchesMap.findSubstr(model.uri); - if (folderMatch) { - folderMatch.bindModel(model); - } + folderMatch?.bindModel(model); } private createFolderMatchWithResource(resource: URI, id: string, index: number, query: ITextQuery): FolderMatchWithResource { @@ -818,9 +814,7 @@ export class SearchResult extends Disposable { } const folderMatch = this.getFolderMatch(raw[0].resource); - if (folderMatch) { - folderMatch.add(raw, silent); - } + folderMatch?.add(raw, silent); }); this._otherFilesMatch?.add(other, silent); diff --git a/src/vs/workbench/contrib/search/test/common/searchModel.test.ts b/src/vs/workbench/contrib/search/test/common/searchModel.test.ts index 104eb095df6..cd49e55a241 100644 --- a/src/vs/workbench/contrib/search/test/common/searchModel.test.ts +++ b/src/vs/workbench/contrib/search/test/common/searchModel.test.ts @@ -113,9 +113,7 @@ suite('SearchModel', () => { function canceleableSearchService(tokenSource: CancellationTokenSource): ISearchService { return { textSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): Promise { - if (token) { - token.onCancellationRequested(() => tokenSource.cancel()); - } + token?.onCancellationRequested(() => tokenSource.cancel()); return new Promise(resolve => { queueMicrotask(() => { diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index ce5ca3eee59..0f288681ede 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -1506,9 +1506,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { throw new Error('Command name should never be undefined here.'); } this._collectVariables(variables, command.name); - if (command.args) { - command.args.forEach(arg => this._collectVariables(variables, arg)); - } + command.args?.forEach(arg => this._collectVariables(variables, arg)); // Try to get a scope. const scope = (task._source).scope; if (scope !== TaskScope.Global) { @@ -1532,9 +1530,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { if (options.shell.executable) { this._collectVariables(variables, options.shell.executable); } - if (options.shell.args) { - options.shell.args.forEach(arg => this._collectVariables(variables, arg)); - } + options.shell.args?.forEach(arg => this._collectVariables(variables, arg)); } } } @@ -1703,8 +1699,6 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { private _appendOutput(output: string): void { const outputChannel = this._outputService.getChannel(this._outputChannelId); - if (outputChannel) { - outputChannel.append(output); - } + outputChannel?.append(output); } } diff --git a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts index cc0907cc5b3..9aa97b5dff9 100644 --- a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts +++ b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts @@ -224,9 +224,7 @@ export abstract class AbstractProblemCollector implements IDisposable { protected removeResourceToClean(owner: string, resource: string): void { const resourceSet = this.resourcesToClean.get(owner); - if (resourceSet) { - resourceSet.delete(resource); - } + resourceSet?.delete(resource); } private getResourceSetToClean(owner: string): Map { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts index ec9e1af2191..ec5a3cb0aa9 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts @@ -123,9 +123,7 @@ export class TerminalFindWidget extends SimpleFindWidget { protected _onFocusTrackerFocus() { const instance = this._terminalService.activeInstance; - if (instance) { - instance.notifyFindWidgetFocusChanged(true); - } + instance?.notifyFindWidgetFocusChanged(true); this._findWidgetFocused.set(true); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts index c294f795fa4..e94818dd002 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts @@ -393,9 +393,7 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { const newIndex = index < this._terminalInstances.length ? index : this._terminalInstances.length - 1; this.setActiveInstanceByIndex(newIndex); // TODO: Only focus the new instance if the group had focus? - if (this.activeInstance) { - this.activeInstance.focus(true); - } + this.activeInstance?.focus(true); } else if (index < this._activeInstanceIndex) { // Adjust active instance index if needed this._activeInstanceIndex--; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts b/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts index 762ca452ce7..edd1bf0aeda 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts @@ -54,9 +54,7 @@ export class TerminalMainContribution extends Disposable implements IWorkbenchCo const instance = terminalService.getInstanceFromResource(resource); if (instance) { const sourceGroup = terminalGroupService.getGroupForInstance(instance); - if (sourceGroup) { - sourceGroup.removeInstance(instance); - } + sourceGroup?.removeInstance(instance); } const resolvedResource = terminalEditorService.resolveResource(instance || resource); const editor = terminalEditorService.getInputFromResource(resolvedResource) || { editor: resolvedResource }; diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedList.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedList.ts index 6fef8b0769f..50144d87e1c 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedList.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedList.ts @@ -115,9 +115,7 @@ export class GettingStartedIndexList { const keys = e.when?.keys(); - if (keys) { - keys.forEach(key => this.contextKeysToWatch.add(key)); - } + keys?.forEach(key => this.contextKeysToWatch.add(key)); }); this.lastRendered = toRender; diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 32551395307..6855bf0628d 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -456,9 +456,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat if (this.restrictedSettings.workspace) { keys.push(...this.restrictedSettings.workspace); } - if (this.restrictedSettings.workspaceFolder) { - this.restrictedSettings.workspaceFolder.forEach((value) => keys.push(...value)); - } + this.restrictedSettings.workspaceFolder?.forEach((value) => keys.push(...value)); keys = distinct(keys); if (keys.length) { this.triggerConfigurationChange({ keys, overrides: [] }, { data, workspace: this.workspace }, ConfigurationTarget.WORKSPACE); diff --git a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts index 868e6da301d..8cbb4be2848 100644 --- a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts @@ -165,9 +165,7 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe let resolvedValue = await this.evaluateSingleVariable(environment, match, variable, folderUri, commandValueMapping); - if (resolvedVariables) { - resolvedVariables.set(variable, resolvedValue); - } + resolvedVariables?.set(variable, resolvedValue); if ((resolvedValue !== match) && types.isString(resolvedValue) && resolvedValue.match(AbstractVariableResolverService.VARIABLE_REGEXP)) { resolvedValue = await this.resolveString(environment, folderUri, resolvedValue, commandValueMapping, resolvedVariables); diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts index 41668ffe3f6..f387703969b 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts @@ -254,9 +254,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost return; } this._isTerminating = true; - if (this._protocol) { - this._protocol.send(createMessageOfType(MessageType.Terminate)); - } + this._protocol?.send(createMessageOfType(MessageType.Terminate)); super.dispose(); } diff --git a/src/vs/workbench/services/extensions/common/rpcProtocol.ts b/src/vs/workbench/services/extensions/common/rpcProtocol.ts index db4b01da54b..76658baa5ce 100644 --- a/src/vs/workbench/services/extensions/common/rpcProtocol.ts +++ b/src/vs/workbench/services/extensions/common/rpcProtocol.ts @@ -306,9 +306,7 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { break; } case MessageType.Acknowledged: { - if (this._logger) { - this._logger.logIncoming(msgLength, req, RequestInitiator.LocalSide, `ack`); - } + this._logger?.logIncoming(msgLength, req, RequestInitiator.LocalSide, `ack`); this._onDidReceiveAcknowledge(req); break; } @@ -357,9 +355,7 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { } private _receiveRequest(msgLength: number, req: number, rpcId: number, method: string, args: any[], usesCancellationToken: boolean): void { - if (this._logger) { - this._logger.logIncoming(msgLength, req, RequestInitiator.OtherSide, `receiveRequest ${getStringIdentifierForProxy(rpcId)}.${method}(`, args); - } + this._logger?.logIncoming(msgLength, req, RequestInitiator.OtherSide, `receiveRequest ${getStringIdentifierForProxy(rpcId)}.${method}(`, args); const callId = String(req); let promise: Promise; @@ -379,40 +375,30 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { // Acknowledge the request const msg = MessageIO.serializeAcknowledged(req); - if (this._logger) { - this._logger.logOutgoing(msg.byteLength, req, RequestInitiator.OtherSide, `ack`); - } + this._logger?.logOutgoing(msg.byteLength, req, RequestInitiator.OtherSide, `ack`); this._protocol.send(msg); promise.then((r) => { delete this._cancelInvokedHandlers[callId]; const msg = MessageIO.serializeReplyOK(req, r, this._uriReplacer); - if (this._logger) { - this._logger.logOutgoing(msg.byteLength, req, RequestInitiator.OtherSide, `reply:`, r); - } + this._logger?.logOutgoing(msg.byteLength, req, RequestInitiator.OtherSide, `reply:`, r); this._protocol.send(msg); }, (err) => { delete this._cancelInvokedHandlers[callId]; const msg = MessageIO.serializeReplyErr(req, err); - if (this._logger) { - this._logger.logOutgoing(msg.byteLength, req, RequestInitiator.OtherSide, `replyErr:`, err); - } + this._logger?.logOutgoing(msg.byteLength, req, RequestInitiator.OtherSide, `replyErr:`, err); this._protocol.send(msg); }); } private _receiveCancel(msgLength: number, req: number): void { - if (this._logger) { - this._logger.logIncoming(msgLength, req, RequestInitiator.OtherSide, `receiveCancel`); - } + this._logger?.logIncoming(msgLength, req, RequestInitiator.OtherSide, `receiveCancel`); const callId = String(req); this._cancelInvokedHandlers[callId]?.(); } private _receiveReply(msgLength: number, req: number, value: any): void { - if (this._logger) { - this._logger.logIncoming(msgLength, req, RequestInitiator.LocalSide, `receiveReply:`, value); - } + this._logger?.logIncoming(msgLength, req, RequestInitiator.LocalSide, `receiveReply:`, value); const callId = String(req); if (!this._pendingRPCReplies.hasOwnProperty(callId)) { return; @@ -425,9 +411,7 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { } private _receiveReplyErr(msgLength: number, req: number, value: any): void { - if (this._logger) { - this._logger.logIncoming(msgLength, req, RequestInitiator.LocalSide, `receiveReplyErr:`, value); - } + this._logger?.logIncoming(msgLength, req, RequestInitiator.LocalSide, `receiveReplyErr:`, value); const callId = String(req); if (!this._pendingRPCReplies.hasOwnProperty(callId)) { @@ -494,9 +478,7 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { if (cancellationToken) { cancellationToken.onCancellationRequested(() => { const msg = MessageIO.serializeCancel(req); - if (this._logger) { - this._logger.logOutgoing(msg.byteLength, req, RequestInitiator.LocalSide, `cancel`); - } + this._logger?.logOutgoing(msg.byteLength, req, RequestInitiator.LocalSide, `cancel`); this._protocol.send(MessageIO.serializeCancel(req)); }); } @@ -504,9 +486,7 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { this._pendingRPCReplies[callId] = result; this._onWillSendRequest(req); const msg = MessageIO.serializeRequest(req, rpcId, methodName, serializedRequestArguments, !!cancellationToken); - if (this._logger) { - this._logger.logOutgoing(msg.byteLength, req, RequestInitiator.LocalSide, `request: ${getStringIdentifierForProxy(rpcId)}.${methodName}(`, args); - } + this._logger?.logOutgoing(msg.byteLength, req, RequestInitiator.LocalSide, `request: ${getStringIdentifierForProxy(rpcId)}.${methodName}(`, args); this._protocol.send(msg); return result; } diff --git a/src/vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost.ts index d498ddd7960..a7a35d0d022 100644 --- a/src/vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost.ts @@ -51,9 +51,7 @@ class ExtHostNamedPipeCommunication extends Disposable implements IExtHostCommun const namedPipeServer = createServer(); namedPipeServer.on('error', reject); namedPipeServer.listen(pipeName, () => { - if (namedPipeServer) { - namedPipeServer.removeListener('error', reject); - } + namedPipeServer?.removeListener('error', reject); resolve({ pipeName, namedPipeServer }); }); this._register(toDisposable(() => { diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index 9fb02061b6e..4fff738137d 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -640,9 +640,7 @@ export class DefaultSettings extends Disposable { settingsGroup.sections[settingsGroup.sections.length - 1].settings = configurationSettings; } } - if (config.allOf) { - config.allOf.forEach(c => this.parseConfig(c, result, configurations, settingsGroup, seenSettings)); - } + config.allOf?.forEach(c => this.parseConfig(c, result, configurations, settingsGroup, seenSettings)); return result; } diff --git a/src/vs/workbench/services/search/common/searchService.ts b/src/vs/workbench/services/search/common/searchService.ts index c40362672e2..f5127405714 100644 --- a/src/vs/workbench/services/search/common/searchService.ts +++ b/src/vs/workbench/services/search/common/searchService.ts @@ -171,13 +171,9 @@ export class SearchService extends Disposable implements ISearchService { private getSchemesInQuery(query: ISearchQuery): Set { const schemes = new Set(); - if (query.folderQueries) { - query.folderQueries.forEach(fq => schemes.add(fq.folder.scheme)); - } + query.folderQueries?.forEach(fq => schemes.add(fq.folder.scheme)); - if (query.extraFileResources) { - query.extraFileResources.forEach(extraFile => schemes.add(extraFile.scheme)); - } + query.extraFileResources?.forEach(extraFile => schemes.add(extraFile.scheme)); return schemes; } diff --git a/src/vs/workbench/services/search/node/rawSearchService.ts b/src/vs/workbench/services/search/node/rawSearchService.ts index 82e31e6f31f..6e9251f016f 100644 --- a/src/vs/workbench/services/search/node/rawSearchService.ts +++ b/src/vs/workbench/services/search/node/rawSearchService.ts @@ -334,9 +334,7 @@ export class SearchService implements IRawSearchService { private doSearch(engine: ISearchEngine, progressCallback: IFileProgressCallback, batchSize: number, token?: CancellationToken): Promise { return new Promise((c, e) => { let batch: IRawFileMatch[] = []; - if (token) { - token.onCancellationRequested(() => engine.cancel()); - } + token?.onCancellationRequested(() => engine.cancel()); engine.search((match) => { if (match) { diff --git a/src/vs/workbench/services/textMate/browser/textMateWorker.ts b/src/vs/workbench/services/textMate/browser/textMateWorker.ts index c4d6299742d..9c4339175eb 100644 --- a/src/vs/workbench/services/textMate/browser/textMateWorker.ts +++ b/src/vs/workbench/services/textMate/browser/textMateWorker.ts @@ -214,9 +214,7 @@ export class TextMateWorker { public async acceptTheme(theme: IRawTheme, colorMap: string[]): Promise { const grammarFactory = await this._grammarFactory; - if (grammarFactory) { - grammarFactory.setTheme(theme, colorMap); - } + grammarFactory?.setTheme(theme, colorMap); } public _setTokens(resource: URI, versionId: number, tokens: Uint8Array): void { From 001d52cf6bbc8a94f752768f6d9e094c770e211f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 15 Jun 2022 09:29:50 -0700 Subject: [PATCH 056/175] Add Symbol.iterator functions for vscode.d.ts collection types (#151806) * Add entries functions for vscode.d.ts collection types Fixes #151802 * Spelling * Enable lib.iterable in vscode.d.ts * Switch to use `Symbol.iterator` instead of `entries` * Use extends Iterable in more places * Fixing testItemCollection types * Fixing exthost testing --- src/tsconfig.vscode-dts.json | 1 + .../api/common/extHostDiagnostics.ts | 9 ++++++- .../api/common/extHostTerminalService.ts | 4 ++++ .../api/common/extHostTypeConverters.ts | 1 + src/vs/workbench/api/common/extHostTypes.ts | 10 +++++++- .../api/test/browser/extHostTesting.test.ts | 8 +++---- .../testing/common/testItemCollection.ts | 24 +++++++++---------- src/vscode-dts/vscode.d.ts | 13 ++++++---- 8 files changed, 48 insertions(+), 22 deletions(-) diff --git a/src/tsconfig.vscode-dts.json b/src/tsconfig.vscode-dts.json index 4ae9bc7643a..b8607658396 100644 --- a/src/tsconfig.vscode-dts.json +++ b/src/tsconfig.vscode-dts.json @@ -14,6 +14,7 @@ "types": [], "lib": [ "es5", + "ES2015.Iterable" ], }, "include": [ diff --git a/src/vs/workbench/api/common/extHostDiagnostics.ts b/src/vs/workbench/api/common/extHostDiagnostics.ts index 3eb7aeb41cc..d0581d21faf 100644 --- a/src/vs/workbench/api/common/extHostDiagnostics.ts +++ b/src/vs/workbench/api/common/extHostDiagnostics.ts @@ -179,9 +179,16 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { } forEach(callback: (uri: URI, diagnostics: ReadonlyArray, collection: DiagnosticCollection) => any, thisArg?: any): void { + this._checkDisposed(); + for (const [uri, values] of this) { + callback.call(thisArg, uri, values, this); + } + } + + *[Symbol.iterator](): IterableIterator<[uri: vscode.Uri, diagnostics: readonly vscode.Diagnostic[]]> { this._checkDisposed(); for (const uri of this.#data.keys()) { - callback.apply(thisArg, [uri, this.get(uri), this]); + yield [uri, this.get(uri)]; } } diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 9a898fcfe4a..f27f16dca52 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -876,6 +876,10 @@ export class EnvironmentVariableCollection implements vscode.EnvironmentVariable this.map.forEach((value, key) => callback.call(thisArg, key, value, this)); } + [Symbol.iterator](): IterableIterator<[variable: string, mutator: vscode.EnvironmentVariableMutator]> { + return this.map.entries(); + } + delete(variable: string): void { this.map.delete(variable); this._onDidChangeCollection.fire(); diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 956ba5dc9c5..9b733dc8951 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1777,6 +1777,7 @@ export namespace TestItem { add: () => { }, delete: () => { }, forEach: () => { }, + *[Symbol.iterator]() { }, get: () => undefined, replace: () => { }, size: 0, diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index b4dfa9ec1de..ce30f68d26e 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2441,7 +2441,7 @@ export class DataTransferItem { } @es5ClassCompat -export class DataTransfer { +export class DataTransfer implements vscode.DataTransfer { #items = new Map(); constructor(init?: Iterable) { @@ -2472,6 +2472,14 @@ export class DataTransfer { } } } + + *[Symbol.iterator](): IterableIterator<[mimeType: string, item: vscode.DataTransferItem]> { + for (const [mime, items] of this.#items) { + for (const item of items) { + yield [mime, item]; + } + } + } } @es5ClassCompat diff --git a/src/vs/workbench/api/test/browser/extHostTesting.test.ts b/src/vs/workbench/api/test/browser/extHostTesting.test.ts index c4ce107acf1..2ab14e477a1 100644 --- a/src/vs/workbench/api/test/browser/extHostTesting.test.ts +++ b/src/vs/workbench/api/test/browser/extHostTesting.test.ts @@ -36,8 +36,8 @@ const assertTreesEqual = (a: TestItemImpl | undefined, b: TestItemImpl | undefin assert.deepStrictEqual(simplify(a), simplify(b)); - const aChildren = [...a.children].map(c => c.id).sort(); - const bChildren = [...b.children].map(c => c.id).sort(); + const aChildren = [...a.children].map(([_, c]) => c.id).sort(); + const bChildren = [...b.children].map(([_, c]) => c.id).sort(); assert.strictEqual(aChildren.length, bChildren.length, `expected ${a.label}.children.length == ${b.label}.children.length`); aChildren.forEach(key => assertTreesEqual(a.children.get(key) as TestItemImpl, b.children.get(key) as TestItemImpl)); }; @@ -242,7 +242,7 @@ suite('ExtHost Testing', () => { const oldA = single.root.children.get('id-a') as TestItemImpl; const newA = new TestItemImpl('ctrlId', 'id-a', 'Hello world', undefined); - newA.children.replace([...oldA.children]); + newA.children.replace([...oldA.children].map(([_, item]) => item)); single.root.children.replace([ newA, new TestItemImpl('ctrlId', 'id-b', single.root.children.get('id-b')!.label, undefined), @@ -334,7 +334,7 @@ suite('ExtHost Testing', () => { }, ]); - assert.deepStrictEqual([...single.root.children], [single.root.children.get('id-a')]); + assert.deepStrictEqual([...single.root.children].map(([_, item]) => item), [single.root.children.get('id-a')]); assert.deepStrictEqual(b.parent, a); }); }); diff --git a/src/vs/workbench/contrib/testing/common/testItemCollection.ts b/src/vs/workbench/contrib/testing/common/testItemCollection.ts index 06ad3b1b4b7..9a72a25c1ac 100644 --- a/src/vs/workbench/contrib/testing/common/testItemCollection.ts +++ b/src/vs/workbench/contrib/testing/common/testItemCollection.ts @@ -134,7 +134,7 @@ const diffTestItems = (a: ITestItem, b: ITestItem) => { return output as Partial | undefined; }; -export interface ITestChildrenLike extends Iterable { +export interface ITestChildrenLike extends Iterable<[string, T]> { get(id: string): T | undefined; delete(id: string): void; } @@ -356,7 +356,7 @@ export class TestItemCollection extends Disposable { this.connectItemAndChildren(actual, internal, parent); // Remove any orphaned children. - for (const child of oldChildren) { + for (const [_, child] of oldChildren) { if (!this.options.getChildren(actual).get(child.id)) { this.removeItem(TestId.joinToString(fullId, child.id)); } @@ -417,7 +417,7 @@ export class TestItemCollection extends Disposable { this.connectItem(actual, internal, parent); // Discover any existing children that might have already been added - for (const child of this.options.getChildren(actual)) { + for (const [_, child] of this.options.getChildren(actual)) { this.upsertItem(child, internal); } } @@ -464,7 +464,7 @@ export class TestItemCollection extends Disposable { } const expandRequests: Promise[] = []; - for (const child of this.options.getChildren(internal.actual)) { + for (const [_, child] of this.options.getChildren(internal.actual)) { const promise = this.expand(TestId.joinToString(internal.fullId, child.id), levels); if (isThenable(promise)) { expandRequests.push(promise); @@ -544,7 +544,7 @@ export class TestItemCollection extends Disposable { } this.tree.delete(item.fullId.toString()); - for (const child of this.options.getChildren(item.actual)) { + for (const [_, child] of this.options.getChildren(item.actual)) { queue.push(this.tree.get(TestId.joinToString(item.fullId, child.id))); } } @@ -561,8 +561,8 @@ export class TestItemCollection extends Disposable { } } -/** Implementation os vscode.TestItemCollection */ -export interface ITestItemChildren extends Iterable { +/** Implementation of vscode.TestItemCollection */ +export interface ITestItemChildren extends Iterable<[string, T]> { readonly size: number; replace(items: readonly T[]): void; forEach(callback: (item: T, collection: this) => unknown, thisArg?: unknown): void; @@ -607,6 +607,11 @@ export const createTestItemChildren = (api: ITestItemAp } }, + /** @inheritdoc */ + [Symbol.iterator](): IterableIterator<[string, T]> { + return mapped.entries(); + }, + /** @inheritdoc */ replace(items: Iterable) { const newMapped = new Map(); @@ -670,10 +675,5 @@ export const createTestItemChildren = (api: ITestItemAp toJSON() { return Array.from(mapped.values()); }, - - /** @inheritdoc */ - [Symbol.iterator]() { - return mapped.values(); - }, }; }; diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index c94a4461885..070cb2adafc 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -5980,7 +5980,7 @@ declare module 'vscode' { * To get an instance of a `DiagnosticCollection` use * {@link languages.createDiagnosticCollection createDiagnosticCollection}. */ - export interface DiagnosticCollection { + export interface DiagnosticCollection extends Iterable<[uri: Uri, diagnostics: readonly Diagnostic[]]> { /** * The name of this diagnostic collection, for instance `typescript`. Every diagnostic @@ -10120,7 +10120,7 @@ declare module 'vscode' { * data transfer. These additional mime types will only be included in the `handleDrop` when the the drag was initiated from * an element in the same drag and drop controller. */ - export class DataTransfer { + export class DataTransfer implements Iterable<[mimeType: string, item: DataTransferItem]> { /** * Retrieves the data transfer item for a given mime type. * @@ -10146,6 +10146,11 @@ declare module 'vscode' { * @param thisArg The `this` context used when invoking the handler function. */ forEach(callbackfn: (value: DataTransferItem, key: string, dataTransfer: DataTransfer) => void, thisArg?: any): void; + + /** + * Get a new iterator with the `[mime, item]` pairs for each element in this data transfer. + */ + [Symbol.iterator](): IterableIterator<[mimeType: string, item: DataTransferItem]>; } /** @@ -10812,7 +10817,7 @@ declare module 'vscode' { /** * A collection of mutations that an extension can apply to a process environment. */ - export interface EnvironmentVariableCollection { + export interface EnvironmentVariableCollection extends Iterable<[variable: string, mutator: EnvironmentVariableMutator]> { /** * Whether the collection should be cached for the workspace and applied to the terminal * across window reloads. When true the collection will be active immediately such when the @@ -15681,7 +15686,7 @@ declare module 'vscode' { * Collection of test items, found in {@link TestItem.children} and * {@link TestController.items}. */ - export interface TestItemCollection { + export interface TestItemCollection extends Iterable<[id: string, testItem: TestItem]> { /** * Gets the number of items in the collection. */ From 98ad4c15b56ab338f96c4a93250334d3926e5c06 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 15 Jun 2022 09:34:32 -0700 Subject: [PATCH 057/175] Switch to std library Object functions (#152128) - `fromMap` -> `Object.fromEntries` - `values` -> `Object.values` --- src/vs/base/common/collections.ts | 27 ------------------- .../workbench/api/common/extHost.api.impl.ts | 3 +-- .../tasks/browser/terminalTaskSystem.ts | 4 +-- .../baseConfigurationResolverService.ts | 4 +-- .../extensions/common/extensionsRegistry.ts | 5 ++-- 5 files changed, 7 insertions(+), 36 deletions(-) diff --git a/src/vs/base/common/collections.ts b/src/vs/base/common/collections.ts index 190492f58f7..1f16cd438ea 100644 --- a/src/vs/base/common/collections.ts +++ b/src/vs/base/common/collections.ts @@ -9,29 +9,12 @@ */ export type IStringDictionary = Record; - /** * An interface for a JavaScript object that * acts a dictionary. The keys are numbers. */ export type INumberDictionary = Record; -const hasOwnProperty = Object.prototype.hasOwnProperty; - -/** - * Returns an array which contains all values that reside - * in the given dictionary. - */ -export function values(from: IStringDictionary | INumberDictionary): T[] { - const result: T[] = []; - for (const key in from) { - if (hasOwnProperty.call(from, key)) { - result.push((from as any)[key]); - } - } - return result; -} - /** * Iterates over each entry in the provided dictionary. The iterator will stop when the callback returns `false`. * @@ -63,16 +46,6 @@ export function groupBy(data: V[], groupF return result; } -export function fromMap(original: Map): IStringDictionary { - const result: IStringDictionary = Object.create(null); - if (original) { - original.forEach((value, key) => { - result[key] = value; - }); - } - return result; -} - export function diffSets(before: Set, after: Set): { removed: T[]; added: T[] } { const removed: T[] = []; const added: T[] = []; diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index cca4ac70549..b438fb90ead 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -51,7 +51,6 @@ import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyId import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import type * as vscode from 'vscode'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { values } from 'vs/base/common/collections'; import { ExtHostEditorInsets } from 'vs/workbench/api/common/extHostCodeInsets'; import { ExtHostLabelService } from 'vs/workbench/api/common/extHostLabelService'; import { getRemoteName } from 'vs/platform/remote/common/remoteHosts'; @@ -186,7 +185,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I rpcProtocol.set(ExtHostContext.ExtHostInteractive, new ExtHostInteractive(rpcProtocol, extHostNotebook, extHostDocumentsAndEditors, extHostCommands)); // Check that no named customers are missing - const expected: ProxyIdentifier[] = values(ExtHostContext); + const expected = Object.values>(ExtHostContext); rpcProtocol.assertRegistered(expected); // Other instances diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index 0f288681ede..a2ddb8bef5c 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -10,7 +10,7 @@ import * as Types from 'vs/base/common/types'; import * as Platform from 'vs/base/common/platform'; import * as Async from 'vs/base/common/async'; import * as resources from 'vs/base/common/resources'; -import { IStringDictionary, values } from 'vs/base/common/collections'; +import { IStringDictionary } from 'vs/base/common/collections'; import { LinkedMap, Touch } from 'vs/base/common/map'; import Severity from 'vs/base/common/severity'; import { Event, Emitter } from 'vs/base/common/event'; @@ -1217,7 +1217,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { if (group) { // Try to find an existing terminal to split. // Even if an existing terminal is found, the split can fail if the terminal width is too small. - for (const terminal of values(this._terminals)) { + for (const terminal of Object.values(this._terminals)) { if (terminal.group === group) { this._logService.trace(`Found terminal to split for group ${group}`); const originalInstance = terminal.terminal; diff --git a/src/vs/workbench/services/configurationResolver/browser/baseConfigurationResolverService.ts b/src/vs/workbench/services/configurationResolver/browser/baseConfigurationResolverService.ts index b20e5d5ead4..059d8e46456 100644 --- a/src/vs/workbench/services/configurationResolver/browser/baseConfigurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/browser/baseConfigurationResolverService.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import * as Types from 'vs/base/common/types'; import { Schemas } from 'vs/base/common/network'; import { SideBySideEditor, EditorResourceAccessor } from 'vs/workbench/common/editor'; -import { IStringDictionary, forEach, fromMap } from 'vs/base/common/collections'; +import { IStringDictionary, forEach } from 'vs/base/common/collections'; import { IConfigurationService, IConfigurationOverrides, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IWorkspaceFolder, IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; @@ -128,7 +128,7 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR if (!mapping) { return null; } else if (mapping.size > 0) { - return this.resolveAnyAsync(folder, config, fromMap(mapping)); + return this.resolveAnyAsync(folder, config, Object.fromEntries(mapping)); } else { return config; } diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts index 1e38aee257a..33eaef0922b 100644 --- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts @@ -14,7 +14,6 @@ import { IMessage } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionIdentifier, IExtensionDescription, EXTENSION_CATEGORIES } from 'vs/platform/extensions/common/extensions'; import { ExtensionKind } from 'vs/platform/environment/common/environment'; import { allApiProposals } from 'vs/workbench/services/extensions/common/extensionsApiProposals'; -import { values } from 'vs/base/common/collections'; import { productSchemaId } from 'vs/platform/product/common/productService'; const schemaRegistry = Registry.as(Extensions.JSONContribution); @@ -236,7 +235,7 @@ export const schema: IJSONSchema = { items: { type: 'string', enum: Object.keys(allApiProposals), - markdownEnumDescriptions: values(allApiProposals) + markdownEnumDescriptions: Object.values(allApiProposals) } }, activationEvents: { @@ -601,7 +600,7 @@ schemaRegistry.registerSchema(productSchemaId, { items: { type: 'string', enum: Object.keys(allApiProposals), - markdownEnumDescriptions: values(allApiProposals) + markdownEnumDescriptions: Object.values(allApiProposals) } }] } From 57037a722805136ca8b9f736d6a250d2433c43e5 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 15 Jun 2022 10:40:30 -0700 Subject: [PATCH 058/175] Fix `` without href stripping html (#152230) Fixes #152170 --- src/vs/base/browser/markdownRenderer.ts | 2 +- src/vs/base/test/browser/markdownRenderer.test.ts | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index a34f4924ba2..2e809e2ad48 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -265,7 +265,7 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende || /^command:(\/\/\/)?_workbench\.downloadResource/i.test(href) ) { // drop the link - a.replaceWith(a.textContent ?? ''); + a.replaceWith(...a.childNodes); } else { let resolvedHref = _href(href, false); if (markdown.baseUri) { diff --git a/src/vs/base/test/browser/markdownRenderer.test.ts b/src/vs/base/test/browser/markdownRenderer.test.ts index 7503a8cd510..3852b15797e 100644 --- a/src/vs/base/test/browser/markdownRenderer.test.ts +++ b/src/vs/base/test/browser/markdownRenderer.test.ts @@ -173,6 +173,14 @@ suite('MarkdownRenderer', () => { `); }); + + test('render icon in without href (#152170)', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: true, supportHtml: true }); + mds.appendMarkdown(`$(sync)`); + + const result: HTMLElement = renderMarkdown(mds).element; + assert.strictEqual(result.innerHTML, `

    `); + }); }); suite('ThemeIcons Support Off', () => { From 38bdf5ab7d4082fdd141da5e44f456eef5e52599 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 15 Jun 2022 19:40:59 +0200 Subject: [PATCH 059/175] adopt to application storage (#152224) --- .../electron-sandbox/extensionTipsService.ts | 8 ++--- .../common/userDataAutoSyncService.ts | 24 +++++++------- .../common/userDataSyncEnablementService.ts | 10 +++--- .../common/userDataSyncMachines.ts | 4 +-- .../common/userDataSyncService.ts | 6 ++-- .../common/userDataSyncStoreService.ts | 32 +++++++++---------- .../electron-sandbox/remoteExtensionsInit.ts | 4 +-- .../browser/webExtensionsScannerService.ts | 4 +-- .../services/userData/browser/userDataInit.ts | 2 +- .../browser/userDataSyncWorkbenchService.ts | 12 +++---- 10 files changed, 53 insertions(+), 53 deletions(-) diff --git a/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts b/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts index a8d9a94f50f..c6e03e0bf5f 100644 --- a/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts +++ b/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts @@ -256,7 +256,7 @@ export class ExtensionTipsService extends BaseExtensionTipsService { } private getLastPromptedMediumExeTime(): number { - let value = this.storageService.getNumber(lastPromptedMediumImpExeTimeStorageKey, StorageScope.GLOBAL); + let value = this.storageService.getNumber(lastPromptedMediumImpExeTimeStorageKey, StorageScope.APPLICATION); if (!value) { value = Date.now(); this.updateLastPromptedMediumExeTime(value); @@ -265,17 +265,17 @@ export class ExtensionTipsService extends BaseExtensionTipsService { } private updateLastPromptedMediumExeTime(value: number): void { - this.storageService.store(lastPromptedMediumImpExeTimeStorageKey, value, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(lastPromptedMediumImpExeTimeStorageKey, value, StorageScope.APPLICATION, StorageTarget.MACHINE); } private getPromptedExecutableTips(): IStringDictionary { - return JSON.parse(this.storageService.get(promptedExecutableTipsStorageKey, StorageScope.GLOBAL, '{}')); + return JSON.parse(this.storageService.get(promptedExecutableTipsStorageKey, StorageScope.APPLICATION, '{}')); } private addToRecommendedExecutables(exeName: string, tips: IExecutableBasedExtensionTip[]) { const promptedExecutableTips = this.getPromptedExecutableTips(); promptedExecutableTips[exeName] = tips.map(({ extensionId }) => extensionId.toLowerCase()); - this.storageService.store(promptedExecutableTipsStorageKey, JSON.stringify(promptedExecutableTips), StorageScope.GLOBAL, StorageTarget.USER); + this.storageService.store(promptedExecutableTipsStorageKey, JSON.stringify(promptedExecutableTips), StorageScope.APPLICATION, StorageTarget.USER); } private groupByInstalled(recommendationsToSuggest: string[], local: ILocalExtension[]): { installed: string[]; uninstalled: string[] } { diff --git a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts index 19db5e01a89..bf59598603b 100644 --- a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts @@ -54,26 +54,26 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto private lastSyncUrl: URI | undefined; private get syncUrl(): URI | undefined { - const value = this.storageService.get(storeUrlKey, StorageScope.GLOBAL); + const value = this.storageService.get(storeUrlKey, StorageScope.APPLICATION); return value ? URI.parse(value) : undefined; } private set syncUrl(syncUrl: URI | undefined) { if (syncUrl) { - this.storageService.store(storeUrlKey, syncUrl.toString(), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(storeUrlKey, syncUrl.toString(), StorageScope.APPLICATION, StorageTarget.MACHINE); } else { - this.storageService.remove(storeUrlKey, StorageScope.GLOBAL); + this.storageService.remove(storeUrlKey, StorageScope.APPLICATION); } } private previousProductQuality: string | undefined; private get productQuality(): string | undefined { - return this.storageService.get(productQualityKey, StorageScope.GLOBAL); + return this.storageService.get(productQualityKey, StorageScope.APPLICATION); } private set productQuality(productQuality: string | undefined) { if (productQuality) { - this.storageService.store(productQualityKey, productQuality, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(productQualityKey, productQuality, StorageScope.APPLICATION, StorageTarget.MACHINE); } else { - this.storageService.remove(productQualityKey, StorageScope.GLOBAL); + this.storageService.remove(productQualityKey, StorageScope.APPLICATION); } } @@ -194,7 +194,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto this.updateEnablement(false); // Reset Session - this.storageService.remove(sessionIdKey, StorageScope.GLOBAL); + this.storageService.remove(sessionIdKey, StorageScope.APPLICATION); // Reset if (everywhere) { @@ -315,7 +315,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto } private async disableMachineEventually(): Promise { - this.storageService.store(disableMachineEventuallyKey, true, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(disableMachineEventuallyKey, true, StorageScope.APPLICATION, StorageTarget.MACHINE); await timeout(1000 * 60 * 10); // Return if got stopped meanwhile. @@ -332,11 +332,11 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto } private hasToDisableMachineEventually(): boolean { - return this.storageService.getBoolean(disableMachineEventuallyKey, StorageScope.GLOBAL, false); + return this.storageService.getBoolean(disableMachineEventuallyKey, StorageScope.APPLICATION, false); } private stopDisableMachineEventually(): void { - this.storageService.remove(disableMachineEventuallyKey, StorageScope.GLOBAL); + this.storageService.remove(disableMachineEventuallyKey, StorageScope.APPLICATION); } private sources: string[] = []; @@ -481,7 +481,7 @@ class AutoSync extends Disposable { } } - const sessionId = this.storageService.get(sessionIdKey, StorageScope.GLOBAL); + const sessionId = this.storageService.get(sessionIdKey, StorageScope.APPLICATION); // Server session is different from client session if (sessionId && this.manifest && sessionId !== this.manifest.session) { if (this.hasSyncServiceChanged()) { @@ -521,7 +521,7 @@ class AutoSync extends Disposable { // Update local session id if (this.manifest && this.manifest.session !== sessionId) { - this.storageService.store(sessionIdKey, this.manifest.session, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(sessionIdKey, this.manifest.session, StorageScope.APPLICATION, StorageTarget.MACHINE); } // Return if cancellation is requested diff --git a/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts b/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts index ac12a674403..dfb2cccaea1 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts @@ -46,7 +46,7 @@ export class UserDataSyncEnablementService extends Disposable implements IUserDa case 'off': return false; } - return this.storageService.getBoolean(enablementKey, StorageScope.GLOBAL, false); + return this.storageService.getBoolean(enablementKey, StorageScope.APPLICATION, false); } canToggleEnablement(): boolean { @@ -58,11 +58,11 @@ export class UserDataSyncEnablementService extends Disposable implements IUserDa return; } this.telemetryService.publicLog2<{ enabled: boolean }, SyncEnablementClassification>(enablementKey, { enabled }); - this.storageService.store(enablementKey, enabled, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(enablementKey, enabled, StorageScope.APPLICATION, StorageTarget.MACHINE); } isResourceEnabled(resource: SyncResource): boolean { - return this.storageService.getBoolean(getEnablementKey(resource), StorageScope.GLOBAL, true); + return this.storageService.getBoolean(getEnablementKey(resource), StorageScope.APPLICATION, true); } setResourceEnablement(resource: SyncResource, enabled: boolean): void { @@ -77,11 +77,11 @@ export class UserDataSyncEnablementService extends Disposable implements IUserDa } private storeResourceEnablement(resourceEnablementKey: string, enabled: boolean): void { - this.storageService.store(resourceEnablementKey, enabled, StorageScope.GLOBAL, isWeb ? StorageTarget.USER /* sync in web */ : StorageTarget.MACHINE); + this.storageService.store(resourceEnablementKey, enabled, StorageScope.APPLICATION, isWeb ? StorageTarget.USER /* sync in web */ : StorageTarget.MACHINE); } private onDidStorageChange(storageChangeEvent: IStorageValueChangeEvent): void { - if (storageChangeEvent.scope !== StorageScope.GLOBAL) { + if (storageChangeEvent.scope !== StorageScope.APPLICATION) { return; } diff --git a/src/vs/platform/userDataSync/common/userDataSyncMachines.ts b/src/vs/platform/userDataSync/common/userDataSyncMachines.ts index 43b71b2ab05..1158727d42c 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncMachines.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncMachines.ts @@ -132,7 +132,7 @@ export class UserDataSyncMachinesService extends Disposable implements IUserData await this.writeMachinesData(machineData); const currentMachineId = await this.currentMachineIdPromise; if (machineId === currentMachineId) { - this.storageService.store(currentMachineNameKey, name, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(currentMachineNameKey, name, StorageScope.APPLICATION, StorageTarget.MACHINE); } } } @@ -149,7 +149,7 @@ export class UserDataSyncMachinesService extends Disposable implements IUserData } private computeCurrentMachineName(machines: IMachineData[]): string { - const previousName = this.storageService.get(currentMachineNameKey, StorageScope.GLOBAL); + const previousName = this.storageService.get(currentMachineNameKey, StorageScope.APPLICATION); if (previousName) { return previousName; } diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index 4845ad05da6..d5e5b74cb7f 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -85,7 +85,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ ) { super(); this.updateStatus([]); - this._lastSyncTime = this.storageService.getNumber(LAST_SYNC_TIME_KEY, StorageScope.GLOBAL, undefined); + this._lastSyncTime = this.storageService.getNumber(LAST_SYNC_TIME_KEY, StorageScope.APPLICATION, undefined); } async createSyncTask(manifest: IUserDataManifest | null, disableCache?: boolean): Promise { @@ -372,7 +372,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ async resetLocal(): Promise { this.checkEnablement(); - this.storageService.remove(LAST_SYNC_TIME_KEY, StorageScope.GLOBAL); + this.storageService.remove(LAST_SYNC_TIME_KEY, StorageScope.APPLICATION); if (this.synchronizers.value) { for (const synchroniser of this.synchronizers.value.enabled) { try { @@ -451,7 +451,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ private updateLastSyncTime(): void { if (this.status === SyncStatus.Idle) { this._lastSyncTime = new Date().getTime(); - this.storageService.store(LAST_SYNC_TIME_KEY, this._lastSyncTime, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(LAST_SYNC_TIME_KEY, this._lastSyncTime, StorageScope.APPLICATION, StorageTarget.MACHINE); this._onDidChangeLastSyncTime.fire(this._lastSyncTime); } } diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts index 8ae695baae4..5d926223d60 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts @@ -44,10 +44,10 @@ export abstract class AbstractUserDataSyncStoreManagementService extends Disposa get userDataSyncStore(): UserDataSyncStore | undefined { return this._userDataSyncStore; } protected get userDataSyncStoreType(): UserDataSyncStoreType | undefined { - return this.storageService.get(SYNC_SERVICE_URL_TYPE, StorageScope.GLOBAL) as UserDataSyncStoreType; + return this.storageService.get(SYNC_SERVICE_URL_TYPE, StorageScope.APPLICATION) as UserDataSyncStoreType; } protected set userDataSyncStoreType(type: UserDataSyncStoreType | undefined) { - this.storageService.store(SYNC_SERVICE_URL_TYPE, type, StorageScope.GLOBAL, isWeb ? StorageTarget.USER /* sync in web */ : StorageTarget.MACHINE); + this.storageService.store(SYNC_SERVICE_URL_TYPE, type, StorageScope.APPLICATION, isWeb ? StorageTarget.USER /* sync in web */ : StorageTarget.MACHINE); } constructor( @@ -57,7 +57,7 @@ export abstract class AbstractUserDataSyncStoreManagementService extends Disposa ) { super(); this.updateUserDataSyncStore(); - this._register(Event.filter(storageService.onDidChangeValue, e => e.key === SYNC_SERVICE_URL_TYPE && e.scope === StorageScope.GLOBAL && this.userDataSyncStoreType !== this.userDataSyncStore?.type)(() => this.updateUserDataSyncStore())); + this._register(Event.filter(storageService.onDidChangeValue, e => e.key === SYNC_SERVICE_URL_TYPE && e.scope === StorageScope.APPLICATION && this.userDataSyncStoreType !== this.userDataSyncStore?.type)(() => this.updateUserDataSyncStore())); } protected updateUserDataSyncStore(): void { @@ -115,16 +115,16 @@ export class UserDataSyncStoreManagementService extends AbstractUserDataSyncStor ) { super(productService, configurationService, storageService); - const previousConfigurationSyncStore = this.storageService.get(SYNC_PREVIOUS_STORE, StorageScope.GLOBAL); + const previousConfigurationSyncStore = this.storageService.get(SYNC_PREVIOUS_STORE, StorageScope.APPLICATION); if (previousConfigurationSyncStore) { this.previousConfigurationSyncStore = JSON.parse(previousConfigurationSyncStore); } const syncStore = this.productService[CONFIGURATION_SYNC_STORE_KEY]; if (syncStore) { - this.storageService.store(SYNC_PREVIOUS_STORE, JSON.stringify(syncStore), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(SYNC_PREVIOUS_STORE, JSON.stringify(syncStore), StorageScope.APPLICATION, StorageTarget.MACHINE); } else { - this.storageService.remove(SYNC_PREVIOUS_STORE, StorageScope.GLOBAL); + this.storageService.remove(SYNC_PREVIOUS_STORE, StorageScope.APPLICATION); } } @@ -202,7 +202,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync } private initDonotMakeRequestsUntil(): void { - const donotMakeRequestsUntil = this.storageService.getNumber(DONOT_MAKE_REQUESTS_UNTIL_KEY, StorageScope.GLOBAL); + const donotMakeRequestsUntil = this.storageService.getNumber(DONOT_MAKE_REQUESTS_UNTIL_KEY, StorageScope.APPLICATION); if (donotMakeRequestsUntil && Date.now() < donotMakeRequestsUntil) { this.setDonotMakeRequestsUntil(new Date(donotMakeRequestsUntil)); } @@ -219,11 +219,11 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync } if (this._donotMakeRequestsUntil) { - this.storageService.store(DONOT_MAKE_REQUESTS_UNTIL_KEY, this._donotMakeRequestsUntil.getTime(), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(DONOT_MAKE_REQUESTS_UNTIL_KEY, this._donotMakeRequestsUntil.getTime(), StorageScope.APPLICATION, StorageTarget.MACHINE); this.resetDonotMakeRequestsUntilPromise = createCancelablePromise(token => timeout(this._donotMakeRequestsUntil!.getTime() - Date.now(), token).then(() => this.setDonotMakeRequestsUntil(undefined))); this.resetDonotMakeRequestsUntilPromise.then(null, e => null /* ignore error */); } else { - this.storageService.remove(DONOT_MAKE_REQUESTS_UNTIL_KEY, StorageScope.GLOBAL); + this.storageService.remove(DONOT_MAKE_REQUESTS_UNTIL_KEY, StorageScope.APPLICATION); } this._onDidChangeDonotMakeRequestsUntil.fire(); @@ -362,7 +362,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync } } - const currentSessionId = this.storageService.get(USER_SESSION_ID_KEY, StorageScope.GLOBAL); + const currentSessionId = this.storageService.get(USER_SESSION_ID_KEY, StorageScope.APPLICATION); if (currentSessionId && manifest && currentSessionId !== manifest.session) { // Server session is different from client session so clear cached session. @@ -376,7 +376,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync if (manifest) { // update session - this.storageService.store(USER_SESSION_ID_KEY, manifest.session, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(USER_SESSION_ID_KEY, manifest.session, StorageScope.APPLICATION, StorageTarget.MACHINE); } return manifest; @@ -397,8 +397,8 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync } private clearSession(): void { - this.storageService.remove(USER_SESSION_ID_KEY, StorageScope.GLOBAL); - this.storageService.remove(MACHINE_SESSION_ID_KEY, StorageScope.GLOBAL); + this.storageService.remove(USER_SESSION_ID_KEY, StorageScope.APPLICATION); + this.storageService.remove(MACHINE_SESSION_ID_KEY, StorageScope.APPLICATION); } private async request(url: string, options: IRequestOptions, successCodes: number[], token: CancellationToken): Promise { @@ -518,14 +518,14 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync } private addSessionHeaders(headers: IHeaders): void { - let machineSessionId = this.storageService.get(MACHINE_SESSION_ID_KEY, StorageScope.GLOBAL); + let machineSessionId = this.storageService.get(MACHINE_SESSION_ID_KEY, StorageScope.APPLICATION); if (machineSessionId === undefined) { machineSessionId = generateUuid(); - this.storageService.store(MACHINE_SESSION_ID_KEY, machineSessionId, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(MACHINE_SESSION_ID_KEY, machineSessionId, StorageScope.APPLICATION, StorageTarget.MACHINE); } headers['X-Machine-Session-Id'] = machineSessionId; - const userSessionId = this.storageService.get(USER_SESSION_ID_KEY, StorageScope.GLOBAL); + const userSessionId = this.storageService.get(USER_SESSION_ID_KEY, StorageScope.APPLICATION); if (userSessionId !== undefined) { headers['X-User-Session-Id'] = userSessionId; } diff --git a/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts b/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts index aa4a1d19536..7209af8ccaf 100644 --- a/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts +++ b/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts @@ -57,11 +57,11 @@ export class RemoteExtensionsInitializerContribution implements IWorkbenchContri } const newRemoteConnectionKey = `${IS_NEW_KEY}.${connection.remoteAuthority}`; // Skip: Not a new remote connection - if (!this.storageService.getBoolean(newRemoteConnectionKey, StorageScope.GLOBAL, true)) { + if (!this.storageService.getBoolean(newRemoteConnectionKey, StorageScope.APPLICATION, true)) { this.logService.trace(`Skipping initializing remote extensions because the window with this remote authority was opened before.`); return; } - this.storageService.store(newRemoteConnectionKey, false, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(newRemoteConnectionKey, false, StorageScope.APPLICATION, StorageTarget.MACHINE); // Skip: Not a new workspace if (!this.storageService.isNew(StorageScope.WORKSPACE)) { this.logService.trace(`Skipping initializing remote extensions because this workspace was opened before.`); diff --git a/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts b/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts index e19fdd2509b..7209305803b 100644 --- a/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts @@ -218,7 +218,7 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten } const result: IScannedExtension[] = []; try { - const useCache = this.storageService.get('additionalBuiltinExtensions', StorageScope.GLOBAL, '[]') === JSON.stringify(extensions); + const useCache = this.storageService.get('additionalBuiltinExtensions', StorageScope.APPLICATION, '[]') === JSON.stringify(extensions); const webExtensions = await (useCache ? this.getCustomBuiltinExtensionsFromCache() : this.updateCustomBuiltinExtensionsCache()); if (webExtensions.length) { await Promise.all(webExtensions.map(async webExtension => { @@ -232,7 +232,7 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten } })); } - this.storageService.store('additionalBuiltinExtensions', JSON.stringify(extensions), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store('additionalBuiltinExtensions', JSON.stringify(extensions), StorageScope.APPLICATION, StorageTarget.MACHINE); } catch (error) { this.logService.info('Ignoring following additional builtin extensions as there is an error while fetching them from gallery', extensions.map(({ id }) => id), getErrorMessage(error)); } diff --git a/src/vs/workbench/services/userData/browser/userDataInit.ts b/src/vs/workbench/services/userData/browser/userDataInit.ts index 404c963b5ff..1d7504a2656 100644 --- a/src/vs/workbench/services/userData/browser/userDataInit.ts +++ b/src/vs/workbench/services/userData/browser/userDataInit.ts @@ -87,7 +87,7 @@ export class UserDataInitializationService implements IUserDataInitializationSer return; } - if (!this.storageService.isNew(StorageScope.GLOBAL)) { + if (!this.storageService.isNew(StorageScope.APPLICATION)) { this.logService.trace(`Skipping initializing user data as application was opened before`); return; } diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts index f0b94a22f71..78c27df5d0c 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts @@ -631,7 +631,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat } private onDidChangeStorage(e: IStorageValueChangeEvent): void { - if (e.key === UserDataSyncWorkbenchService.CACHED_SESSION_STORAGE_KEY && e.scope === StorageScope.GLOBAL + if (e.key === UserDataSyncWorkbenchService.CACHED_SESSION_STORAGE_KEY && e.scope === StorageScope.APPLICATION && this.currentSessionId !== this.getStoredCachedSessionId() /* This checks if current window changed the value or not */) { this._cachedCurrentSessionId = null; this.update(); @@ -651,24 +651,24 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat this._cachedCurrentSessionId = cachedSessionId; if (cachedSessionId === undefined) { this.logService.info('Settings Sync: Reset current session'); - this.storageService.remove(UserDataSyncWorkbenchService.CACHED_SESSION_STORAGE_KEY, StorageScope.GLOBAL); + this.storageService.remove(UserDataSyncWorkbenchService.CACHED_SESSION_STORAGE_KEY, StorageScope.APPLICATION); } else { this.logService.info('Settings Sync: Updated current session', cachedSessionId); - this.storageService.store(UserDataSyncWorkbenchService.CACHED_SESSION_STORAGE_KEY, cachedSessionId, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(UserDataSyncWorkbenchService.CACHED_SESSION_STORAGE_KEY, cachedSessionId, StorageScope.APPLICATION, StorageTarget.MACHINE); } } } private getStoredCachedSessionId(): string | undefined { - return this.storageService.get(UserDataSyncWorkbenchService.CACHED_SESSION_STORAGE_KEY, StorageScope.GLOBAL); + return this.storageService.get(UserDataSyncWorkbenchService.CACHED_SESSION_STORAGE_KEY, StorageScope.APPLICATION); } private get useWorkbenchSessionId(): boolean { - return !this.storageService.getBoolean(UserDataSyncWorkbenchService.DONOT_USE_WORKBENCH_SESSION_STORAGE_KEY, StorageScope.GLOBAL, false); + return !this.storageService.getBoolean(UserDataSyncWorkbenchService.DONOT_USE_WORKBENCH_SESSION_STORAGE_KEY, StorageScope.APPLICATION, false); } private set useWorkbenchSessionId(useWorkbenchSession: boolean) { - this.storageService.store(UserDataSyncWorkbenchService.DONOT_USE_WORKBENCH_SESSION_STORAGE_KEY, !useWorkbenchSession, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(UserDataSyncWorkbenchService.DONOT_USE_WORKBENCH_SESSION_STORAGE_KEY, !useWorkbenchSession, StorageScope.APPLICATION, StorageTarget.MACHINE); } } From 13a80e03e2593a42ec2f9e6ec3c99abe4a99fc3d Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 15 Jun 2022 10:49:36 -0700 Subject: [PATCH 060/175] Last pass using ?.() for method call (#152231) Follow up on f17b33faf21feba942e4663e40226456b081ae5d This handles sightly more complex cases, changing: ```ts if (a) { a.b.c(); } ``` to: ```ts a?.b.c(); ``` --- src/vs/base/browser/ui/actionbar/actionViewItems.ts | 12 +++--------- src/vs/base/browser/ui/dialog/dialog.ts | 4 +--- src/vs/base/browser/ui/list/listView.ts | 8 ++------ src/vs/base/browser/ui/menu/menubar.ts | 4 +--- src/vs/base/browser/ui/splitview/paneview.ts | 4 +--- .../code/electron-sandbox/issue/issueReporterMain.ts | 8 ++------ src/vs/editor/browser/widget/codeEditorWidget.ts | 4 +--- .../contrib/documentSymbols/browser/outlineModel.ts | 4 +--- .../parameterHints/browser/parameterHintsWidget.ts | 8 ++------ .../platform/issue/electron-main/issueMainService.ts | 4 +--- src/vs/workbench/api/browser/mainThreadProgress.ts | 4 +--- src/vs/workbench/api/common/extHostCodeInsets.ts | 4 +--- src/vs/workbench/api/common/extHostComments.ts | 4 +--- src/vs/workbench/api/common/extHostDiagnostics.ts | 12 +++--------- .../parts/notifications/notificationsToasts.ts | 4 +--- src/vs/workbench/browser/parts/panel/panelPart.ts | 4 +--- .../browser/parts/statusbar/statusbarModel.ts | 8 ++------ src/vs/workbench/contrib/debug/common/debugUtils.ts | 4 +--- .../notebook/browser/view/cellParts/cellOutput.ts | 4 +--- .../browser/view/renderers/webviewPreloads.ts | 4 +--- .../preferences/browser/preferencesWidgets.ts | 8 ++------ .../preferences/browser/settingsTreeModels.ts | 4 +--- src/vs/workbench/contrib/scm/browser/scmViewPane.ts | 4 +--- .../workbench/contrib/search/browser/searchView.ts | 4 +--- .../workbench/contrib/watermark/browser/watermark.ts | 4 +--- 25 files changed, 34 insertions(+), 102 deletions(-) diff --git a/src/vs/base/browser/ui/actionbar/actionViewItems.ts b/src/vs/base/browser/ui/actionbar/actionViewItems.ts index 61340ea1ba2..a41daab42af 100644 --- a/src/vs/base/browser/ui/actionbar/actionViewItems.ts +++ b/src/vs/base/browser/ui/actionbar/actionViewItems.ts @@ -367,9 +367,7 @@ export class ActionViewItem extends BaseActionViewItem { this.updateEnabled(); } else { - if (this.label) { - this.label.classList.remove('codicon'); - } + this.label?.classList.remove('codicon'); } } @@ -380,18 +378,14 @@ export class ActionViewItem extends BaseActionViewItem { this.label.classList.remove('disabled'); } - if (this.element) { - this.element.classList.remove('disabled'); - } + this.element?.classList.remove('disabled'); } else { if (this.label) { this.label.setAttribute('aria-disabled', 'true'); this.label.classList.add('disabled'); } - if (this.element) { - this.element.classList.add('disabled'); - } + this.element?.classList.add('disabled'); } } diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts index 4076b02b51e..b8f106ef7af 100644 --- a/src/vs/base/browser/ui/dialog/dialog.ts +++ b/src/vs/base/browser/ui/dialog/dialog.ts @@ -427,9 +427,7 @@ export class Dialog extends Disposable { this.element.style.backgroundColor = bgColor?.toString() ?? ''; this.element.style.border = border; - if (this.buttonBar) { - this.buttonBar.buttons.forEach(button => button.style(style)); - } + this.buttonBar?.buttons.forEach(button => button.style(style)); this.checkbox?.style(style); diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 8612a2f213a..c776ecec38b 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -1110,9 +1110,7 @@ export class ListView implements ISpliceable, IDisposable { const item = this.items[index]!; item.dropTarget = true; - if (item.row) { - item.row.domNode.classList.add('drop-target'); - } + item.row?.domNode.classList.add('drop-target'); } this.currentDragFeedbackDisposable = toDisposable(() => { @@ -1120,9 +1118,7 @@ export class ListView implements ISpliceable, IDisposable { const item = this.items[index]!; item.dropTarget = false; - if (item.row) { - item.row.domNode.classList.remove('drop-target'); - } + item.row?.domNode.classList.remove('drop-target'); } }); } diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts index 521a65e212e..907684665ed 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -956,9 +956,7 @@ export class MenuBar extends Disposable { } if (this.focusedMenu.holder) { - if (this.focusedMenu.holder.parentElement) { - this.focusedMenu.holder.parentElement.classList.remove('open'); - } + this.focusedMenu.holder.parentElement?.classList.remove('open'); this.focusedMenu.holder.remove(); } diff --git a/src/vs/base/browser/ui/splitview/paneview.ts b/src/vs/base/browser/ui/splitview/paneview.ts index fc646081f2a..659eb9bd8fd 100644 --- a/src/vs/base/browser/ui/splitview/paneview.ts +++ b/src/vs/base/browser/ui/splitview/paneview.ts @@ -143,9 +143,7 @@ export abstract class Pane extends Disposable implements IView { return false; } - if (this.element) { - this.element.classList.toggle('expanded', expanded); - } + this.element?.classList.toggle('expanded', expanded); this._expanded = !!expanded; this.updateHeader(); diff --git a/src/vs/code/electron-sandbox/issue/issueReporterMain.ts b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts index 8b62af66a09..4037fbb3441 100644 --- a/src/vs/code/electron-sandbox/issue/issueReporterMain.ts +++ b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts @@ -1204,12 +1204,8 @@ export class IssueReporter extends Disposable { // helper functions function hide(el: Element | undefined | null) { - if (el) { - el.classList.add('hidden'); - } + el?.classList.add('hidden'); } function show(el: Element | undefined | null) { - if (el) { - el.classList.remove('hidden'); - } + el?.classList.remove('hidden'); } diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 43e5e06a4d7..75810f5c95e 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -588,9 +588,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } public setHiddenAreas(ranges: IRange[]): void { - if (this._modelData) { - this._modelData.viewModel.setHiddenAreas(ranges.map(r => Range.lift(r))); - } + this._modelData?.viewModel.setHiddenAreas(ranges.map(r => Range.lift(r))); } public getVisibleColumnFromPosition(rawPosition: IPosition): number { diff --git a/src/vs/editor/contrib/documentSymbols/browser/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/browser/outlineModel.ts index a2959557316..773a83b80b9 100644 --- a/src/vs/editor/contrib/documentSymbols/browser/outlineModel.ts +++ b/src/vs/editor/contrib/documentSymbols/browser/outlineModel.ts @@ -30,9 +30,7 @@ export abstract class TreeElement { abstract parent: TreeElement | undefined; remove(): void { - if (this.parent) { - this.parent.children.delete(this.id); - } + this.parent?.children.delete(this.id); } static findId(candidate: DocumentSymbol | string, container: TreeElement): string { diff --git a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts index 43aeb4c6509..3396636e3be 100644 --- a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts @@ -159,9 +159,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { this.keyVisible.set(true); this.visible = true; setTimeout(() => { - if (this.domNodes) { - this.domNodes.element.classList.add('visible'); - } + this.domNodes?.element.classList.add('visible'); }, 100); this.editor.layoutContentWidget(this); } @@ -176,9 +174,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { this.keyVisible.reset(); this.visible = false; this.announcedLabel = null; - if (this.domNodes) { - this.domNodes.element.classList.remove('visible'); - } + this.domNodes?.element.classList.remove('visible'); this.editor.layoutContentWidget(this); } diff --git a/src/vs/platform/issue/electron-main/issueMainService.ts b/src/vs/platform/issue/electron-main/issueMainService.ts index 31884bfdbaa..24cc5db2fab 100644 --- a/src/vs/platform/issue/electron-main/issueMainService.ts +++ b/src/vs/platform/issue/electron-main/issueMainService.ts @@ -168,9 +168,7 @@ export class IssueMainService implements ICommonIssueService { throw new Error(`Unexpected command source: ${from}`); } - if (parentWindow) { - parentWindow.webContents.send('vscode:runAction', { id, from, args }); - } + parentWindow?.webContents.send('vscode:runAction', { id, from, args }); }); validatedIpcMain.on('vscode:openExternal', (_: unknown, arg: string) => { diff --git a/src/vs/workbench/api/browser/mainThreadProgress.ts b/src/vs/workbench/api/browser/mainThreadProgress.ts index 7fa98113c30..beec4789ac9 100644 --- a/src/vs/workbench/api/browser/mainThreadProgress.ts +++ b/src/vs/workbench/api/browser/mainThreadProgress.ts @@ -57,9 +57,7 @@ export class MainThreadProgress implements MainThreadProgressShape { $progressReport(handle: number, message: IProgressStep): void { const entry = this._progress.get(handle); - if (entry) { - entry.progress.report(message); - } + entry?.progress.report(message); } $progressEnd(handle: number): void { diff --git a/src/vs/workbench/api/common/extHostCodeInsets.ts b/src/vs/workbench/api/common/extHostCodeInsets.ts index e91a8eae63c..5caf786ffa0 100644 --- a/src/vs/workbench/api/common/extHostCodeInsets.ts +++ b/src/vs/workbench/api/common/extHostCodeInsets.ts @@ -134,8 +134,6 @@ export class ExtHostEditorInsets implements ExtHostEditorInsetsShape { $onDidReceiveMessage(handle: number, message: any): void { const value = this._insets.get(handle); - if (value) { - value.onDidReceiveMessage.fire(message); - } + value?.onDidReceiveMessage.fire(message); } } diff --git a/src/vs/workbench/api/common/extHostComments.ts b/src/vs/workbench/api/common/extHostComments.ts index b46d02fd634..2259201d139 100644 --- a/src/vs/workbench/api/common/extHostComments.ts +++ b/src/vs/workbench/api/common/extHostComments.ts @@ -172,9 +172,7 @@ export function createExtHostComments(mainContext: IMainContext, commands: ExtHo $deleteCommentThread(commentControllerHandle: number, commentThreadHandle: number) { const commentController = this._commentControllers.get(commentControllerHandle); - if (commentController) { - commentController.$deleteCommentThread(commentThreadHandle); - } + commentController?.$deleteCommentThread(commentThreadHandle); } $provideCommentingRanges(commentControllerHandle: number, uriComponents: UriComponents, token: CancellationToken): Promise { diff --git a/src/vs/workbench/api/common/extHostDiagnostics.ts b/src/vs/workbench/api/common/extHostDiagnostics.ts index d0581d21faf..81cf1f2b756 100644 --- a/src/vs/workbench/api/common/extHostDiagnostics.ts +++ b/src/vs/workbench/api/common/extHostDiagnostics.ts @@ -41,9 +41,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { dispose(): void { if (!this._isDisposed) { this.#onDidChangeDiagnostics.fire([...this.#data.keys()]); - if (this.#proxy) { - this.#proxy.$clear(this._owner); - } + this.#proxy?.$clear(this._owner); this.#data.clear(); this._isDisposed = true; } @@ -164,18 +162,14 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { this._checkDisposed(); this.#onDidChangeDiagnostics.fire([uri]); this.#data.delete(uri); - if (this.#proxy) { - this.#proxy.$changeMany(this._owner, [[uri, undefined]]); - } + this.#proxy?.$changeMany(this._owner, [[uri, undefined]]); } clear(): void { this._checkDisposed(); this.#onDidChangeDiagnostics.fire([...this.#data.keys()]); this.#data.clear(); - if (this.#proxy) { - this.#proxy.$clear(this._owner); - } + this.#proxy?.$clear(this._owner); } forEach(callback: (uri: URI, diagnostics: ReadonlyArray, collection: DiagnosticCollection) => any, thisArg?: any): void { diff --git a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index 0215c0f5aa4..ab589ba87e0 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts @@ -367,9 +367,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast } private doHide(): void { - if (this.notificationsToastsContainer) { - this.notificationsToastsContainer.classList.remove('visible'); - } + this.notificationsToastsContainer?.classList.remove('visible'); // Context Key this.notificationsToastsVisibleContextKey.set(false); diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 9fbf579fa27..0409199c6d2 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -717,9 +717,7 @@ export abstract class BasePanelPart extends CompositePart impleme private emptyPanelMessageElement: HTMLElement | undefined; private layoutEmptyMessage(): void { - if (this.emptyPanelMessageElement) { - this.emptyPanelMessageElement.classList.toggle('visible', this.compositeBar.getVisibleComposites().length === 0); - } + this.emptyPanelMessageElement?.classList.toggle('visible', this.compositeBar.getVisibleComposites().length === 0); } private getViewContainer(id: string): ViewContainer | undefined { diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts b/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts index 4d6c56e5b32..600d79dc989 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts @@ -406,13 +406,9 @@ export class StatusbarViewModel extends Disposable { } // Mark: first visible item - if (firstVisibleItem) { - firstVisibleItem.container.classList.add('first-visible-item'); - } + firstVisibleItem?.container.classList.add('first-visible-item'); // Mark: last visible item - if (lastVisibleItem) { - lastVisibleItem.container.classList.add('last-visible-item'); - } + lastVisibleItem?.container.classList.add('last-visible-item'); } } diff --git a/src/vs/workbench/contrib/debug/common/debugUtils.ts b/src/vs/workbench/contrib/debug/common/debugUtils.ts index 6b58a993556..66325b37e09 100644 --- a/src/vs/workbench/contrib/debug/common/debugUtils.ts +++ b/src/vs/workbench/contrib/debug/common/debugUtils.ts @@ -260,9 +260,7 @@ function convertPaths(msg: DebugProtocol.ProtocolMessage, fixSourcePath: (toDA: case 'disassemble': { const di = response; - if (di.body) { - di.body.instructions.forEach(di => fixSourcePath(false, di.location)); - } + di.body?.instructions.forEach(di => fixSourcePath(false, di.location)); } break; default: diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts index cd410d4a510..181777ec7d4 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts @@ -92,9 +92,7 @@ export class CellOutputElement extends Disposable { } detach() { - if (this.renderedOutputContainer) { - this.renderedOutputContainer.parentElement?.removeChild(this.renderedOutputContainer); - } + this.renderedOutputContainer?.parentElement?.removeChild(this.renderedOutputContainer); let count = 0; if (this.innerContainer) { 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 65f85230512..52dd982f0d3 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -776,9 +776,7 @@ async function webviewPreloads(ctx: PreloadContext) { highlightCurrentMatch(index: number) { const oldMatch = this.matches[this._findMatchIndex]; - if (oldMatch) { - oldMatch.highlightResult?.update(matchColor, oldMatch.isShadow ? undefined : 'find-match'); - } + oldMatch?.highlightResult?.update(matchColor, oldMatch.isShadow ? undefined : 'find-match'); const match = this.matches[index]; this._findMatchIndex = index; diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts index 7c4112dca63..cab53e0d6a6 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts @@ -463,15 +463,11 @@ export class SearchWidget extends Widget { layout(dimension: DOM.Dimension) { if (dimension.width < 400) { - if (this.countElement) { - this.countElement.classList.add('hide'); - } + this.countElement?.classList.add('hide'); this.inputBox.inputElement.style.paddingRight = '0px'; } else { - if (this.countElement) { - this.countElement.classList.remove('hide'); - } + this.countElement?.classList.remove('hide'); this.inputBox.inputElement.style.paddingRight = this.getControlsWidth() + 'px'; } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index 6b7badf59d3..b7b89c030d7 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -808,9 +808,7 @@ export class SearchResultModel extends SettingsTreeModel { const localMatchKeys = new Set(); const localResult = this.rawSearchResults[SearchResultIdx.Local]; - if (localResult) { - localResult.filterMatches.forEach(m => localMatchKeys.add(m.setting.key)); - } + localResult?.filterMatches.forEach(m => localMatchKeys.add(m.setting.key)); const remoteResult = this.rawSearchResults[SearchResultIdx.Remote]; if (remoteResult) { diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 47468bfe3bc..8d824b6a5fd 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -2380,9 +2380,7 @@ export class SCMViewPane extends ViewPane { if (e.editorOptions.pinned) { const activeEditorPane = this.editorService.activeEditorPane; - if (activeEditorPane) { - activeEditorPane.group.pinEditor(activeEditorPane.input); - } + activeEditorPane?.group.pinEditor(activeEditorPane.input); } } diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index e872e6f61eb..8f87fe9247d 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -395,9 +395,7 @@ export class SearchView extends ViewPane { } // Enable highlights if there are searchresults - if (this.viewModel) { - this.viewModel.searchResult.toggleHighlights(visible); - } + this.viewModel?.searchResult.toggleHighlights(visible); } get searchAndReplaceWidget(): SearchWidget { diff --git a/src/vs/workbench/contrib/watermark/browser/watermark.ts b/src/vs/workbench/contrib/watermark/browser/watermark.ts index cb1fe2362b2..d20cf69cc08 100644 --- a/src/vs/workbench/contrib/watermark/browser/watermark.ts +++ b/src/vs/workbench/contrib/watermark/browser/watermark.ts @@ -197,9 +197,7 @@ export class WatermarkContribution extends Disposable implements IWorkbenchContr this.watermark.remove(); const container = this.layoutService.getContainer(Parts.EDITOR_PART); - if (container) { - container.classList.remove('has-watermark'); - } + container?.classList.remove('has-watermark'); this.watermarkDisposable.clear(); } From 5f696f9955d0fbf855a336342ddd8ce5a90ad8fc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 15 Jun 2022 20:07:42 +0200 Subject: [PATCH 061/175] fix smoke tests on linux (#152232) --- .../linux/product-build-linux-client-test.yml | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/build/azure-pipelines/linux/product-build-linux-client-test.yml b/build/azure-pipelines/linux/product-build-linux-client-test.yml index 48985fa539d..9627b1e8b93 100644 --- a/build/azure-pipelines/linux/product-build-linux-client-test.yml +++ b/build/azure-pipelines/linux/product-build-linux-client-test.yml @@ -122,23 +122,24 @@ steps: timeoutInMinutes: 20 displayName: Run smoke tests (Browser, Chromium) - - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: - - script: | - set -e - APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - yarn smoketest-no-compile --tracing --build "$APP_PATH" - timeoutInMinutes: 20 - displayName: Run smoke tests (Electron) + # TODO enable again after https://github.com/microsoft/vscode/issues/152143 is fixed + # - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: + # - script: | + # set -e + # APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) + # yarn smoketest-no-compile --tracing --build "$APP_PATH" + # timeoutInMinutes: 20 + # displayName: Run smoke tests (Electron) - - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: - - script: | - set -e - yarn gulp compile-extension:vscode-test-resolver - APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ - yarn smoketest-no-compile --tracing --remote --build "$APP_PATH" - timeoutInMinutes: 20 - displayName: Run smoke tests (Remote) + # - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: + # - script: | + # set -e + # yarn gulp compile-extension:vscode-test-resolver + # APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) + # VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ + # yarn smoketest-no-compile --tracing --remote --build "$APP_PATH" + # timeoutInMinutes: 20 + # displayName: Run smoke tests (Remote) - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: - script: | From 1c2afc392d78606a60b903d5e1ef171b1e006311 Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Wed, 15 Jun 2022 11:25:39 -0700 Subject: [PATCH 062/175] Simplify setting indicators (#152091) Ref #151787 --- .../browser/media/settingsEditor2.css | 4 +-- .../settingsEditorSettingIndicators.ts | 26 ++++++++++++------- .../preferences/browser/settingsTree.ts | 4 +-- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css index 20f1af21202..c32f22f0870 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css @@ -353,12 +353,12 @@ .settings-editor > .settings-body .settings-tree-container .setting-item-contents .setting-item-title .setting-item-ignored .codicon, .settings-editor > .settings-body .settings-tree-container .setting-item-contents .setting-item-title .setting-item-default-overridden .codicon { - vertical-align: text-top; + vertical-align: middle; padding-left: 1px; } .settings-editor > .settings-body .settings-tree-container .setting-item-contents .setting-item-title .setting-item-label .codicon { - vertical-align: text-top; + vertical-align: middle; } .settings-editor > .settings-body .settings-tree-container .setting-item-contents .setting-item-title .setting-item-overrides a.modified-scope { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts index aac1b348f1c..fff6d5a1420 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts @@ -11,7 +11,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { getIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge'; -import { getDefaultIgnoredSettings } from 'vs/platform/userDataSync/common/userDataSync'; +import { getDefaultIgnoredSettings, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; const $ = DOM.$; @@ -22,7 +22,7 @@ export interface ISettingOverrideClickEvent { } /** - * Renders the indicators next to a setting, such as Sync Ignored, Also Modified In, etc. + * Renders the indicators next to a setting, such as "Also Modified In". */ export class SettingsTreeIndicatorsLabel { /** @@ -34,7 +34,9 @@ export class SettingsTreeIndicatorsLabel { private defaultOverrideIndicatorElement: HTMLElement; private defaultOverrideIndicatorLabel: SimpleIconLabel; - constructor(container: HTMLElement) { + constructor( + container: HTMLElement, + @IUserDataSyncEnablementService private readonly userDataSyncEnablementService: IUserDataSyncEnablementService) { this.labelElement = DOM.append(container, $('.misc-label')); this.labelElement.style.display = 'inline'; @@ -53,7 +55,7 @@ export class SettingsTreeIndicatorsLabel { private createSyncIgnoredElement(): HTMLElement { const syncIgnoredElement = $('span.setting-item-ignored'); const syncIgnoredLabel = new SimpleIconLabel(syncIgnoredElement); - syncIgnoredLabel.text = `$(sync-ignored) ${localize('extensionSyncIgnoredLabel', 'Sync: Ignored')}`; + syncIgnoredLabel.text = '$(info) ' + localize('extensionSyncIgnoredLabel', 'Setting not synced'); syncIgnoredLabel.title = localize('syncIgnoredTitle', "Settings sync does not sync this setting"); return syncIgnoredElement; } @@ -76,7 +78,7 @@ export class SettingsTreeIndicatorsLabel { DOM.append(this.labelElement, $('span', undefined, '(')); for (let i = 0; i < elementsToShow.length - 1; i++) { DOM.append(this.labelElement, elementsToShow[i]); - DOM.append(this.labelElement, $('span.comma', undefined, ', ')); + DOM.append(this.labelElement, $('span.comma', undefined, ' • ')); } DOM.append(this.labelElement, elementsToShow[elementsToShow.length - 1]); DOM.append(this.labelElement, $('span', undefined, ')')); @@ -84,7 +86,8 @@ export class SettingsTreeIndicatorsLabel { } updateSyncIgnored(element: SettingsTreeSettingElement, ignoredSettings: string[]) { - this.syncIgnoredElement.style.display = ignoredSettings.includes(element.setting.key) ? 'inline' : 'none'; + this.syncIgnoredElement.style.display = this.userDataSyncEnablementService.isEnabled() + && ignoredSettings.includes(element.setting.key) ? 'inline' : 'none'; this.render(); } @@ -133,7 +136,7 @@ export class SettingsTreeIndicatorsLabel { } if (sourceToDisplay) { this.defaultOverrideIndicatorLabel.title = localize('defaultOverriddenDetails', "Default setting value overridden by {0}", sourceToDisplay); - this.defaultOverrideIndicatorLabel.text = `$(replace) ${sourceToDisplay}`; + this.defaultOverrideIndicatorLabel.text = '$(info) ' + localize('defaultOverriddenLabel', "Default value changed"); } } this.render(); @@ -161,11 +164,14 @@ export function getIndicatorsLabelAriaLabel(element: SettingsTreeSettingElement, // Add default override indicator text if (element.defaultValueSource) { const defaultValueSource = element.defaultValueSource; + let sourceToDisplay = ''; if (typeof defaultValueSource !== 'string' && defaultValueSource.id !== element.setting.extensionInfo?.id) { - const extensionSource = defaultValueSource.displayName ?? defaultValueSource.id; - ariaLabelSections.push(localize('defaultOverriddenDetails', "Default setting value overridden by {0}", extensionSource)); + sourceToDisplay = defaultValueSource.displayName ?? defaultValueSource.id; } else if (typeof defaultValueSource === 'string') { - ariaLabelSections.push(localize('defaultOverriddenDetails', "Default setting value overridden by {0}", defaultValueSource)); + sourceToDisplay = defaultValueSource; + } + if (sourceToDisplay) { + ariaLabelSections.push(localize('defaultOverriddenDetails', "Default setting value overridden by {0}", sourceToDisplay)); } } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 5e905799eef..fcf032fd281 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -785,7 +785,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre const categoryElement = DOM.append(labelCategoryContainer, $('span.setting-item-category')); const labelElementContainer = DOM.append(labelCategoryContainer, $('span.setting-item-label')); const labelElement = new SimpleIconLabel(labelElementContainer); - const indicatorsLabel = new SettingsTreeIndicatorsLabel(titleElement); + const indicatorsLabel = this._instantiationService.createInstance(SettingsTreeIndicatorsLabel, titleElement); const descriptionElement = DOM.append(container, $('.setting-item-description')); const modifiedIndicatorElement = DOM.append(container, $('.setting-item-modified-indicator')); @@ -1814,7 +1814,7 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre const categoryElement = DOM.append(titleElement, $('span.setting-item-category')); const labelElementContainer = DOM.append(titleElement, $('span.setting-item-label')); const labelElement = new SimpleIconLabel(labelElementContainer); - const indicatorsLabel = new SettingsTreeIndicatorsLabel(titleElement); + const indicatorsLabel = this._instantiationService.createInstance(SettingsTreeIndicatorsLabel, titleElement); const descriptionAndValueElement = DOM.append(container, $('.setting-item-value-description')); const controlElement = DOM.append(descriptionAndValueElement, $('.setting-item-bool-control')); From e4c3bca7a29fbe2c7c1cca47022fdf93811346ce Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 15 Jun 2022 11:28:46 -0700 Subject: [PATCH 063/175] Kill all terminals after each shell integration test --- .../smoke/src/areas/terminal/terminal-shellIntegration.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts b/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts index 0bc30da5b58..0e90b784815 100644 --- a/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts +++ b/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Application, Terminal, SettingsEditor, TerminalCommandIdWithValue } from '../../../../automation'; +import { Application, Terminal, SettingsEditor, TerminalCommandIdWithValue, TerminalCommandId } from '../../../../automation'; import { setTerminalTestSettings } from './terminal-helpers'; export function setup() { @@ -21,6 +21,7 @@ export function setup() { }); after(async function () { + await app.workbench.terminal.runCommand(TerminalCommandId.KillAll); await settingsEditor.clearUserSettings(); }); From 7548fed110e06d127f522a794e77083ebc87508f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 15 Jun 2022 11:30:24 -0700 Subject: [PATCH 064/175] after -> afterEach --- test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts b/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts index 0e90b784815..720c0df2177 100644 --- a/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts +++ b/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts @@ -20,7 +20,7 @@ export function setup() { await setTerminalTestSettings(app); }); - after(async function () { + afterEach(async function () { await app.workbench.terminal.runCommand(TerminalCommandId.KillAll); await settingsEditor.clearUserSettings(); }); From 18408b909006d18e2e08e8b1c50d20b31854b644 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 15 Jun 2022 12:35:53 -0700 Subject: [PATCH 065/175] Allow tests to run multiple times --- .../terminal-shellIntegration.test.ts | 51 ++++++++++--------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts b/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts index 720c0df2177..24dc769beb8 100644 --- a/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts +++ b/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts @@ -7,29 +7,32 @@ import { Application, Terminal, SettingsEditor, TerminalCommandIdWithValue, Term import { setTerminalTestSettings } from './terminal-helpers'; export function setup() { - describe('Terminal Shell Integration', () => { - let terminal: Terminal; - let settingsEditor: SettingsEditor; - let app: Application; - // Acquire automation API - before(async function () { - app = this.app as Application; - terminal = app.workbench.terminal; - settingsEditor = app.workbench.settingsEditor; - await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.enabled', 'true'); - await setTerminalTestSettings(app); - }); + for (let i = 0; i < 100; i++) { + describe(`Terminal Shell Integration ${i}`, () => { + let terminal: Terminal; + let settingsEditor: SettingsEditor; + let app: Application; + // Acquire automation API + before(async function () { + app = this.app as Application; + terminal = app.workbench.terminal; + settingsEditor = app.workbench.settingsEditor; + await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.enabled', 'true'); + await setTerminalTestSettings(app); + }); - afterEach(async function () { - await app.workbench.terminal.runCommand(TerminalCommandId.KillAll); - await settingsEditor.clearUserSettings(); - }); + after(async function () { + await settingsEditor.clearUserSettings(); + }); - async function createShellIntegrationProfile() { - await terminal.runCommandWithValue(TerminalCommandIdWithValue.NewWithProfile, process.platform === 'win32' ? 'PowerShell' : 'bash'); - } + afterEach(async function () { + await app.workbench.terminal.runCommand(TerminalCommandId.KillAll); + }); + + async function createShellIntegrationProfile() { + await terminal.runCommandWithValue(TerminalCommandIdWithValue.NewWithProfile, process.platform === 'win32' ? 'PowerShell' : 'bash'); + } - for (let i = 0; i < 100; i++) { describe(`Shell integration ${i}`, function () { describe('Decorations', function () { describe('Should show default icons', function () { @@ -39,7 +42,7 @@ export function setup() { }); it('Success', async () => { await createShellIntegrationProfile(); - await terminal.runCommandInTerminal(`ls`); + await terminal.runCommandInTerminal(`echo "success"`); await terminal.assertCommandDecorations({ placeholder: 1, success: 1, error: 0 }); }); it('Error', async () => { @@ -52,7 +55,7 @@ export function setup() { it('Should update and show custom icons', async () => { await createShellIntegrationProfile(); await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 0 }); - await terminal.runCommandInTerminal(`ls`); + await terminal.runCommandInTerminal(`echo "success"`); await terminal.runCommandInTerminal(`fsdkfsjdlfksjdkf`); await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationIcon', '"zap"'); await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationIconSuccess', '"zap"'); @@ -62,6 +65,6 @@ export function setup() { }); }); }); - } - }); + }); + } } From d19ac496df108aba3f48bfe33804d027a8944d68 Mon Sep 17 00:00:00 2001 From: Alan Ren Date: Wed, 15 Jun 2022 13:50:26 -0700 Subject: [PATCH 066/175] add aria-description support for selectbox --- src/vs/base/browser/ui/selectBox/selectBox.ts | 1 + src/vs/base/browser/ui/selectBox/selectBoxCustom.ts | 4 ++++ src/vs/base/browser/ui/selectBox/selectBoxNative.ts | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/src/vs/base/browser/ui/selectBox/selectBox.ts b/src/vs/base/browser/ui/selectBox/selectBox.ts index 55e23987440..fbf46c262fe 100644 --- a/src/vs/base/browser/ui/selectBox/selectBox.ts +++ b/src/vs/base/browser/ui/selectBox/selectBox.ts @@ -40,6 +40,7 @@ export interface ISelectBoxDelegate extends IDisposable { export interface ISelectBoxOptions { useCustomDrawn?: boolean; ariaLabel?: string; + ariaDescription?: string; minBottomMargin?: number; optionsAsChildren?: boolean; } diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index e4f0a07538d..d076412e705 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -126,6 +126,10 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi this.selectElement.setAttribute('aria-label', this.selectBoxOptions.ariaLabel); } + if (this.selectBoxOptions.ariaDescription) { + this.selectElement.setAttribute('aria-description', this.selectBoxOptions.ariaDescription); + } + this._onDidSelect = new Emitter(); this._register(this._onDidSelect); diff --git a/src/vs/base/browser/ui/selectBox/selectBoxNative.ts b/src/vs/base/browser/ui/selectBox/selectBoxNative.ts index 7248cef367e..73d32697489 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxNative.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxNative.ts @@ -35,6 +35,10 @@ export class SelectBoxNative extends Disposable implements ISelectBoxDelegate { this.selectElement.setAttribute('aria-label', this.selectBoxOptions.ariaLabel); } + if (this.selectBoxOptions.ariaDescription) { + this.selectElement.setAttribute('aria-description', this.selectBoxOptions.ariaDescription); + } + this._onDidSelect = this._register(new Emitter()); this.styles = styles; From 49b05c51f896d1d60ef124bd16bab146eedb91cd Mon Sep 17 00:00:00 2001 From: Alan Ren Date: Wed, 15 Jun 2022 14:14:46 -0700 Subject: [PATCH 067/175] type check --- src/vs/base/browser/ui/selectBox/selectBoxCustom.ts | 2 +- src/vs/base/browser/ui/selectBox/selectBoxNative.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index d076412e705..3f571712fdd 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -126,7 +126,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi this.selectElement.setAttribute('aria-label', this.selectBoxOptions.ariaLabel); } - if (this.selectBoxOptions.ariaDescription) { + if (typeof this.selectBoxOptions.ariaDescription === 'string') { this.selectElement.setAttribute('aria-description', this.selectBoxOptions.ariaDescription); } diff --git a/src/vs/base/browser/ui/selectBox/selectBoxNative.ts b/src/vs/base/browser/ui/selectBox/selectBoxNative.ts index 73d32697489..7a43184de2b 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxNative.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxNative.ts @@ -35,7 +35,7 @@ export class SelectBoxNative extends Disposable implements ISelectBoxDelegate { this.selectElement.setAttribute('aria-label', this.selectBoxOptions.ariaLabel); } - if (this.selectBoxOptions.ariaDescription) { + if (typeof this.selectBoxOptions.ariaDescription === 'string') { this.selectElement.setAttribute('aria-description', this.selectBoxOptions.ariaDescription); } From 37e70da6d0f119f1637864371ef22cde07dd954e Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 15 Jun 2022 23:20:29 +0200 Subject: [PATCH 068/175] Implememts intra-line diffing & merging. --- src/vs/base/common/arrays.ts | 12 + .../contrib/mergeEditor/browser/colors.ts | 28 ++ .../mergeEditor/browser/diffComputer.ts | 89 ++++ .../mergeEditor/browser/media/mergeEditor.css | 4 + .../mergeEditor/browser/mergeEditor.ts | 112 +++-- .../mergeEditor/browser/mergeEditorModel.ts | 200 +++++---- .../contrib/mergeEditor/browser/model.ts | 388 +++++++++++------- .../mergeEditor/browser/textModelDiffs.ts | 123 +++--- .../contrib/mergeEditor/browser/utils.ts | 16 +- 9 files changed, 640 insertions(+), 332 deletions(-) create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/diffComputer.ts diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index cc567305a59..b567c8fe977 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -677,6 +677,18 @@ export function compareBy(selector: (item: TItem) => TCompare return (a, b) => comparator(selector(a), selector(b)); } +export function tieBreakComparators(...comparators: Comparator[]): Comparator { + return (item1, item2) => { + for (const comparator of comparators) { + const result = comparator(item1, item2); + if (!CompareResult.isNeitherLessOrGreaterThan(result)) { + return result; + } + } + return CompareResult.neitherLessOrGreaterThan; + }; +} + /** * The natural order on numbers. */ diff --git a/src/vs/workbench/contrib/mergeEditor/browser/colors.ts b/src/vs/workbench/contrib/mergeEditor/browser/colors.ts index 1eb2f15c408..9760a7f83f9 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/colors.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/colors.ts @@ -62,6 +62,34 @@ export const diffInput1 = registerColor( ) ); +export const diff = registerColor( + 'mergeEditor.diff', + { + dark: '#d3d3d321', + light: '#d3d3d321', + hcDark: '#d3d3d321', + hcLight: '#d3d3d321', + }, + localize( + 'mergeEditor.diff', + 'The foreground color for changes in the result.' + ) +); + +export const diffWord = registerColor( + 'mergeEditor.diff.word', + { + dark: '#e571db21', + light: '#e571db21', + hcDark: '#e571db21', + hcLight: '#e571db21', + }, + localize( + 'mergeEditor.diff.word', + 'The foreground color for word changes in the result.' + ) +); + export const diffInput2 = registerColor( 'mergeEditor.diff.input2', { diff --git a/src/vs/workbench/contrib/mergeEditor/browser/diffComputer.ts b/src/vs/workbench/contrib/mergeEditor/browser/diffComputer.ts new file mode 100644 index 00000000000..e468085027c --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/diffComputer.ts @@ -0,0 +1,89 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Range } from 'vs/editor/common/core/range'; +import { ICharChange, IDiffComputationResult, ILineChange } from 'vs/editor/common/diff/diffComputer'; +import { ITextModel } from 'vs/editor/common/model'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; +import { LineRange, LineRangeMapping, RangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model'; + +export interface IDiffComputer { + computeDiff(textModel1: ITextModel, textModel2: ITextModel): Promise; +} + +export interface IDiffComputerResult { + diffs: LineRangeMapping[] | null; +} + +export class EditorWorkerServiceDiffComputer implements IDiffComputer { + constructor(@IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService) { } + + async computeDiff(textModel1: ITextModel, textModel2: ITextModel): Promise { + const diffs = await this.editorWorkerService.computeDiff(textModel1.uri, textModel2.uri, false, 1000); + if (!diffs || diffs.quitEarly) { + return { diffs: null }; + } + return { diffs: EditorWorkerServiceDiffComputer.fromDiffComputationResult(diffs, textModel1, textModel2) }; + } + + public static fromDiffComputationResult(result: IDiffComputationResult, textModel1: ITextModel, textModel2: ITextModel): LineRangeMapping[] { + return result.changes.map((c) => fromLineChange(c, textModel1, textModel2)); + } +} + +function fromLineChange(lineChange: ILineChange, originalTextModel: ITextModel, modifiedTextModel: ITextModel): LineRangeMapping { + let originalRange: LineRange; + if (lineChange.originalEndLineNumber === 0) { + // Insertion + originalRange = new LineRange(lineChange.originalStartLineNumber + 1, 0); + } else { + originalRange = new LineRange(lineChange.originalStartLineNumber, lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1); + } + + let modifiedRange: LineRange; + if (lineChange.modifiedEndLineNumber === 0) { + // Deletion + modifiedRange = new LineRange(lineChange.modifiedStartLineNumber + 1, 0); + } else { + modifiedRange = new LineRange(lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1); + } + + let innerDiffs = lineChange.charChanges?.map(c => rangeMappingFromCharChange(c)); + if (!innerDiffs) { + innerDiffs = [rangeMappingFromLineRanges(originalRange, modifiedRange)]; + } + + return new LineRangeMapping( + originalTextModel, + originalRange, + modifiedTextModel, + modifiedRange, + innerDiffs + ); +} + +function rangeMappingFromLineRanges(originalRange: LineRange, modifiedRange: LineRange): RangeMapping { + return new RangeMapping( + new Range( + originalRange.startLineNumber, + 1, + originalRange.endLineNumberExclusive, + 1, + ), + new Range( + modifiedRange.startLineNumber, + 1, + modifiedRange.endLineNumberExclusive, + 1, + ) + ); +} + +function rangeMappingFromCharChange(charChange: ICharChange): RangeMapping { + return new RangeMapping( + new Range(charChange.originalStartLineNumber, charChange.originalStartColumn, charChange.originalEndLineNumber, charChange.originalEndColumn), + new Range(charChange.modifiedStartLineNumber, charChange.modifiedStartColumn, charChange.modifiedEndLineNumber, charChange.modifiedEndColumn) + ); +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css b/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css index 6b09151c147..ab4189bc484 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css +++ b/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css @@ -54,6 +54,10 @@ background-color: var(--vscode-mergeEditor-modifiedBaseRange-combination); } +.merge-editor-modified-base-range { + background-color: var(--vscode-mergeEditor-diff); +} + .gutter-item { position: absolute; } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts index 38250239a2a..ae4b7fbe490 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts @@ -9,7 +9,7 @@ import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { Orientation, Sizing } from 'vs/base/browser/ui/splitview/splitview'; import { Toggle } from 'vs/base/browser/ui/toggle/toggle'; import { IAction } from 'vs/base/common/actions'; -import { CompareResult, findLast } from 'vs/base/common/arrays'; +import { CompareResult } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Codicon } from 'vs/base/common/codicons'; import { Color } from 'vs/base/common/color'; @@ -47,7 +47,7 @@ import { applyTextEditorOptions } from 'vs/workbench/common/editor/editorOptions import { autorun, autorunWithStore, derivedObservable, IObservable, ITransaction, keepAlive, ObservableValue, transaction } from 'vs/workbench/contrib/audioCues/browser/observable'; import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; import { MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorModel'; -import { LineRange, ModifiedBaseRange } from 'vs/workbench/contrib/mergeEditor/browser/model'; +import { DocumentMapping, LineRange, SimpleLineRangeMapping, ToggleState } from 'vs/workbench/contrib/mergeEditor/browser/model'; import { applyObservableDecorations, join, n, ReentrancyBarrier, setStyle } from 'vs/workbench/contrib/mergeEditor/browser/utils'; import { settingsSashBorder } from 'vs/workbench/contrib/preferences/common/settingsEditorColorRegistry'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -109,8 +109,19 @@ export class MergeEditor extends AbstractTextEditor { return undefined; } const resultDiffs = model.resultDiffs.read(reader); - const modifiedBaseRanges = ModifiedBaseRange.fromDiffs(model.base, model.input1, model.input1LinesDiffs.read(reader), model.result, resultDiffs); - return modifiedBaseRanges; + const modifiedBaseRanges = DocumentMapping.fromDiffs(model.input1LinesDiffs.read(reader), resultDiffs, model.input1.getLineCount()); + + return new DocumentMapping( + modifiedBaseRanges.lineRangeMappings.map((m) => + m.inputRange.isEmpty || m.outputRange.isEmpty + ? new SimpleLineRangeMapping( + m.inputRange.deltaStart(-1), + m.outputRange.deltaStart(-1) + ) + : m + ), + modifiedBaseRanges.inputLineCount + ); }); const input2ResultMapping = derivedObservable('input2ResultMapping', reader => { const model = this.input2View.model.read(reader); @@ -118,8 +129,19 @@ export class MergeEditor extends AbstractTextEditor { return undefined; } const resultDiffs = model.resultDiffs.read(reader); - const modifiedBaseRanges = ModifiedBaseRange.fromDiffs(model.base, model.input2, model.input2LinesDiffs.read(reader), model.result, resultDiffs); - return modifiedBaseRanges; + const modifiedBaseRanges = DocumentMapping.fromDiffs(model.input2LinesDiffs.read(reader), resultDiffs, model.input2.getLineCount()); + + return new DocumentMapping( + modifiedBaseRanges.lineRangeMappings.map((m) => + m.inputRange.isEmpty || m.outputRange.isEmpty + ? new SimpleLineRangeMapping( + m.inputRange.deltaStart(-1), + m.outputRange.deltaStart(-1) + ) + : m + ), + modifiedBaseRanges.inputLineCount + ); }); this._register(keepAlive(input1ResultMapping)); @@ -390,7 +412,7 @@ export class MergeEditor extends AbstractTextEditor { } } -function synchronizeScrolling(scrollingEditor: CodeEditorWidget, targetEditor: CodeEditorWidget, mapping: ModifiedBaseRange[] | undefined, sourceNumber: 1 | 2) { +function synchronizeScrolling(scrollingEditor: CodeEditorWidget, targetEditor: CodeEditorWidget, mapping: DocumentMapping | undefined, sourceNumber: 1 | 2) { if (!mapping) { return; } @@ -401,25 +423,27 @@ function synchronizeScrolling(scrollingEditor: CodeEditorWidget, targetEditor: C } const topLineNumber = visibleRanges[0].startLineNumber - 1; - const firstBefore = findLast(mapping, r => r.getInputRange(sourceNumber).startLineNumber <= topLineNumber); let sourceRange: LineRange; let targetRange: LineRange; - const targetNumber = sourceNumber === 1 ? 2 : 1; - - const firstBeforeSourceRange = firstBefore?.getInputRange(sourceNumber); - const firstBeforeTargetRange = firstBefore?.getInputRange(targetNumber); - - if (firstBeforeSourceRange && firstBeforeSourceRange.contains(topLineNumber)) { - sourceRange = firstBeforeSourceRange; - targetRange = firstBeforeTargetRange!; - } else if (firstBeforeSourceRange && firstBeforeSourceRange.isEmpty && firstBeforeSourceRange.startLineNumber === topLineNumber) { - sourceRange = firstBeforeSourceRange.deltaEnd(1); - targetRange = firstBeforeTargetRange!.deltaEnd(1); + if (sourceNumber === 1) { + const number = mapping.getOutputLine(topLineNumber); + if (typeof number === 'number') { + sourceRange = new LineRange(topLineNumber, 1); + targetRange = new LineRange(number, 1); + } else { + sourceRange = number.inputRange; + targetRange = number.outputRange; + } } else { - const delta = firstBeforeSourceRange ? firstBeforeTargetRange!.endLineNumberExclusive - firstBeforeSourceRange.endLineNumberExclusive : 0; - sourceRange = new LineRange(topLineNumber, 1); - targetRange = new LineRange(topLineNumber + delta, 1); + const number = mapping.getInputLine(topLineNumber); + if (typeof number === 'number') { + sourceRange = new LineRange(topLineNumber, 1); + targetRange = new LineRange(number, 1); + } else { + sourceRange = number.outputRange; + targetRange = number.inputRange; + } } // sourceRange contains topLineNumber! @@ -532,6 +556,21 @@ class InputCodeEditorView extends CodeEditorView { description: 'Base Range Projection' } }); + + const inputDiffs = m.getInputDiffs(this.inputNumber); + for (const diff of inputDiffs) { + if (diff.innerRangeMappings) { + for (const d of diff.innerRangeMappings) { + result.push({ + range: d.outputRange, + options: { + className: `merge-editor-diff-input${this.inputNumber}`, + description: 'Base Range Projection' + } + }); + } + } + } } } return result; @@ -585,7 +624,7 @@ class InputCodeEditorView extends CodeEditorView { interface ModifiedBaseRangeGutterItemInfo extends IGutterItemInfo { enabled: IObservable; - toggleState: IObservable; + toggleState: IObservable; setState(value: boolean, tx: ITransaction): void; } @@ -606,14 +645,14 @@ class MergeConflictGutterItemView extends Disposable implements IGutterItemView< autorun((reader) => { const item = this.item.read(reader)!; const value = item.toggleState.read(reader); - checkBox.setIcon( - value === true - ? Codicon.check - : value === false - ? undefined - : Codicon.circleFilled - ); - checkBox.checked = value === true; + const iconMap: Record = { + [ToggleState.unset]: { icon: undefined, checked: false }, + [ToggleState.conflicting]: { icon: Codicon.circleFilled, checked: false }, + [ToggleState.first]: { icon: Codicon.check, checked: true }, + [ToggleState.second]: { icon: Codicon.checkAll, checked: true }, + }; + checkBox.setIcon(iconMap[value].icon); + checkBox.checked = iconMap[value].checked; if (!item.enabled.read(reader)) { checkBox.disable(); @@ -658,17 +697,17 @@ class ResultCodeEditorView extends CodeEditorView { model.modifiedBaseRanges.read(reader), model.resultDiffs.read(reader), (baseRange, diff) => - baseRange.baseRange.touches(diff.originalRange) + baseRange.baseRange.touches(diff.inputRange) ? CompareResult.neitherLessOrGreaterThan : LineRange.compareByStart( baseRange.baseRange, - diff.originalRange + diff.inputRange ) ); for (const m of baseRangeWithStoreAndTouchingDiffs) { for (const r of m.rights) { - const range = r.modifiedRange; + const range = r.outputRange; const state = m.left ? model.getState(m.left).read(reader) : undefined; @@ -687,8 +726,11 @@ class ResultCodeEditorView extends CodeEditorView { if (state.input2 && !state.input1) { return 'merge-editor-modified-base-range-input2'; } + if (state.input1 && state.input2) { + return 'merge-editor-modified-base-range-combination'; + } } - return 'merge-editor-modified-base-range-combination'; + return 'merge-editor-modified-base-range'; })(), description: 'Result Diff' } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts index 0d1907a4eb9..16cd2dbb5c0 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts @@ -3,15 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { compareBy, CompareResult, equals } from 'vs/base/common/arrays'; +import { compareBy, CompareResult, tieBreakComparators, equals, numberComparator } from 'vs/base/common/arrays'; import { BugIndicatingError } from 'vs/base/common/errors'; +import { splitLines } from 'vs/base/common/strings'; +import { Constants } from 'vs/base/common/uint'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { EditorModel } from 'vs/workbench/common/editor/editorModel'; import { autorunHandleChanges, derivedObservable, derivedObservableWithCache, IObservable, ITransaction, keepAlive, ObservableValue, transaction, waitForState } from 'vs/workbench/contrib/audioCues/browser/observable'; -import { LineDiff, LineEdit, LineRange, ModifiedBaseRange, ModifiedBaseRangeState } from 'vs/workbench/contrib/mergeEditor/browser/model'; -import { EditorWorkerServiceDiffComputer, TextModelDiffChangeReason, TextModelDiffs, TextModelDiffState } from 'vs/workbench/contrib/mergeEditor/browser/textModelDiffs'; -import { leftJoin } from 'vs/workbench/contrib/mergeEditor/browser/utils'; +import { EditorWorkerServiceDiffComputer } from 'vs/workbench/contrib/mergeEditor/browser/diffComputer'; +import { LineRangeMapping, LineRangeEdit, LineRange, ModifiedBaseRange, ModifiedBaseRangeState, RangeEdit } from 'vs/workbench/contrib/mergeEditor/browser/model'; +import { TextModelDiffChangeReason, TextModelDiffs, TextModelDiffState } from 'vs/workbench/contrib/mergeEditor/browser/textModelDiffs'; +import { concatArrays, leftJoin, elementAtOrUndefined } from 'vs/workbench/contrib/mergeEditor/browser/utils'; export const enum MergeEditorModelState { initializing = 1, @@ -109,22 +114,22 @@ export class MergeEditorModel extends EditorModel { }); } - private recomputeState(resultDiffs: LineDiff[], stores: Map>): void { + private recomputeState(resultDiffs: LineRangeMapping[], stores: Map>): void { transaction(tx => { const baseRangeWithStoreAndTouchingDiffs = leftJoin( stores, resultDiffs, (baseRange, diff) => - baseRange[0].baseRange.touches(diff.originalRange) + baseRange[0].baseRange.touches(diff.inputRange) ? CompareResult.neitherLessOrGreaterThan : LineRange.compareByStart( baseRange[0].baseRange, - diff.originalRange + diff.inputRange ) ); for (const row of baseRangeWithStoreAndTouchingDiffs) { - row.left[1].set(computeState(row.left[0], row.rights), tx); + row.left[1].set(this.computeState(row.left[0], row.rights), tx); } }); } @@ -193,65 +198,73 @@ export class MergeEditorModel extends EditorModel { this.resultTextModelDiffs.applyEditRelativeToOriginal(edit, transaction); } } + + private computeState(baseRange: ModifiedBaseRange, conflictingDiffs: LineRangeMapping[]): ModifiedBaseRangeState { + if (conflictingDiffs.length === 0) { + return ModifiedBaseRangeState.default; + } + const conflictingEdits = conflictingDiffs.map((d) => d.getLineEdit()); + + function editsAgreeWithDiffs(diffs: readonly LineRangeMapping[]): boolean { + return equals( + conflictingEdits, + diffs.map((d) => d.getLineEdit()), + (a, b) => a.equals(b) + ); + } + + if (editsAgreeWithDiffs(baseRange.input1Diffs)) { + return ModifiedBaseRangeState.default.withInput1(true); + } + if (editsAgreeWithDiffs(baseRange.input2Diffs)) { + return ModifiedBaseRangeState.default.withInput2(true); + } + + const states = [ + ModifiedBaseRangeState.default.withInput1(true).withInput2(true), + ModifiedBaseRangeState.default.withInput2(true).withInput1(true), + ]; + + for (const s of states) { + const { edit } = getEditForBase(baseRange, s); + if (edit) { + const resultRange = this.resultTextModelDiffs.getResultRange(baseRange.baseRange); + const existingLines = resultRange.getLines(this.result); + + if (equals(edit.newLines, existingLines, (a, b) => a === b)) { + return s; + } + } + } + + return ModifiedBaseRangeState.conflicting; + } + } -function getEditForBase(baseRange: ModifiedBaseRange, state: ModifiedBaseRangeState): { edit: LineEdit | undefined; effectiveState: ModifiedBaseRangeState } { - interface LineDiffWithInputNumber { - diff: LineDiff; - inputNumber: 1 | 2; - } +function getEditForBase(baseRange: ModifiedBaseRange, state: ModifiedBaseRangeState): { edit: LineRangeEdit | undefined; effectiveState: ModifiedBaseRangeState } { + const diffs = concatArrays( + state.input1 && baseRange.input1CombinedDiff ? [{ diff: baseRange.input1CombinedDiff, inputNumber: 1 as const }] : [], + state.input2 && baseRange.input2CombinedDiff ? [{ diff: baseRange.input2CombinedDiff, inputNumber: 2 as const }] : [], + ); - const diffs = new Array(); - if (state.input1) { - if (baseRange.input1CombinedDiff) { - diffs.push({ diff: baseRange.input1CombinedDiff, inputNumber: 1 }); - } - } - if (state.input2) { - if (baseRange.input2CombinedDiff) { - diffs.push({ diff: baseRange.input2CombinedDiff, inputNumber: 2 }); - } - } if (state.input2First) { diffs.reverse(); } - const firstDiff: LineDiffWithInputNumber | undefined = diffs[0]; - const secondDiff: LineDiffWithInputNumber | undefined = diffs[1]; - diffs.sort(compareBy(d => d.diff.originalRange, LineRange.compareByStart)); + + const firstDiff = elementAtOrUndefined(diffs, 0); + const secondDiff = elementAtOrUndefined(diffs, 1); if (!firstDiff) { return { edit: undefined, effectiveState: ModifiedBaseRangeState.default }; } - if (!secondDiff) { return { edit: firstDiff.diff.getLineEdit(), effectiveState: ModifiedBaseRangeState.default.withInputValue(firstDiff.inputNumber, true) }; } - // Two inserts - if ( - firstDiff.diff.originalRange.lineCount === 0 && - firstDiff.diff.originalRange.equals(secondDiff.diff.originalRange) - ) { - return { - edit: new LineEdit( - firstDiff.diff.originalRange, - firstDiff.diff - .getLineEdit() - .newLines.concat(secondDiff.diff.getLineEdit().newLines) - ), - effectiveState: state, - }; - } - - // Technically non-conflicting diffs - if (diffs.length === 2 && diffs[0].diff.originalRange.endLineNumberExclusive === diffs[1].diff.originalRange.startLineNumber) { - return { - edit: new LineEdit( - LineRange.join(diffs.map(d => d.diff.originalRange))!, - diffs.flatMap(d => d.diff.getLineEdit().newLines) - ), - effectiveState: state, - }; + const result = combineInputs(baseRange, state.input2First ? 2 : 1); + if (result) { + return { edit: result, effectiveState: state }; } return { @@ -263,26 +276,67 @@ function getEditForBase(baseRange: ModifiedBaseRange, state: ModifiedBaseRangeSt }; } -function computeState(baseRange: ModifiedBaseRange, conflictingDiffs: LineDiff[]): ModifiedBaseRangeState { - if (conflictingDiffs.length === 0) { - return ModifiedBaseRangeState.default; - } - const conflictingEdits = conflictingDiffs.map((d) => d.getLineEdit()); +function combineInputs(baseRange: ModifiedBaseRange, firstInput: 1 | 2): LineRangeEdit | undefined { + const combinedDiffs = concatArrays( + baseRange.input1Diffs.flatMap((diffs) => + diffs.innerRangeMappings.map((diff) => ({ diff, input: 1 as const })) + ), + baseRange.input2Diffs.flatMap((diffs) => + diffs.innerRangeMappings.map((diff) => ({ diff, input: 2 as const })) + ) + ).sort( + tieBreakComparators( + compareBy((d) => d.diff.inputRange, Range.compareRangesUsingStarts), + compareBy((d) => (d.input === firstInput ? 1 : 2), numberComparator) + ) + ); - function editsAgreeWithDiffs(diffs: readonly LineDiff[]): boolean { - return equals( - conflictingEdits, - diffs.map((d) => d.getLineEdit()), - (a, b) => a.equals(b) - ); - } + const sortedEdits = combinedDiffs.map(d => { + const sourceTextModel = d.input === 1 ? baseRange.input1TextModel : baseRange.input2TextModel; + return new RangeEdit(d.diff.inputRange, sourceTextModel.getValueInRange(d.diff.outputRange)); + }); - if (editsAgreeWithDiffs(baseRange.input1Diffs)) { - return ModifiedBaseRangeState.default.withInput1(true); - } - if (editsAgreeWithDiffs(baseRange.input2Diffs)) { - return ModifiedBaseRangeState.default.withInput2(true); - } - - return ModifiedBaseRangeState.conflicting; + return editsToLineRangeEdit(baseRange.baseRange, sortedEdits, baseRange.baseTextModel); +} + +function editsToLineRangeEdit(range: LineRange, sortedEdits: RangeEdit[], textModel: ITextModel): LineRangeEdit | undefined { + let text = ''; + const startsLineBefore = range.startLineNumber > 1; + let currentPosition = startsLineBefore + ? new Position( + range.startLineNumber - 1, + Constants.MAX_SAFE_SMALL_INTEGER + ) + : new Position(range.startLineNumber, 1); + + for (const edit of sortedEdits) { + const diffStart = edit.range.getStartPosition(); + if (!currentPosition.isBeforeOrEqual(diffStart)) { + return undefined; + } + const originalText = textModel.getValueInRange(Range.fromPositions(currentPosition, diffStart)); + text += originalText; + text += edit.newText; + currentPosition = edit.range.getEndPosition(); + } + + const endsLineAfter = range.endLineNumberExclusive <= textModel.getLineCount(); + const end = endsLineAfter ? new Position( + range.endLineNumberExclusive, + 1 + ) : new Position(range.endLineNumberExclusive - 1, Constants.MAX_SAFE_SMALL_INTEGER); + + const originalText = textModel.getValueInRange( + Range.fromPositions(currentPosition, end) + ); + text += originalText; + + const lines = splitLines(text); + if (startsLineBefore) { + lines.shift(); + } + if (endsLineAfter) { + lines.pop(); + } + return new LineRangeEdit(range, lines); } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model.ts b/src/vs/workbench/contrib/mergeEditor/browser/model.ts index 21bb807161c..49d94a340dc 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model.ts @@ -3,23 +3,22 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Comparator, compareBy, equals, numberComparator } from 'vs/base/common/arrays'; +import { Comparator, compareBy, equals, findLast, numberComparator } from 'vs/base/common/arrays'; import { BugIndicatingError } from 'vs/base/common/errors'; import { Range } from 'vs/editor/common/core/range'; -import { ILineChange } from 'vs/editor/common/diff/diffComputer'; import { ITextModel } from 'vs/editor/common/model'; /** * Represents an edit, expressed in whole lines: * At {@link LineRange.startLineNumber}, delete {@link LineRange.lineCount} many lines and insert {@link newLines}. */ -export class LineEdit { +export class LineRangeEdit { constructor( public readonly range: LineRange, public readonly newLines: string[] ) { } - public equals(other: LineEdit): boolean { + public equals(other: LineRangeEdit): boolean { return this.range.equals(other.range) && equals(this.newLines, other.newLines); } @@ -28,8 +27,19 @@ export class LineEdit { } } +export class RangeEdit { + constructor( + public readonly range: Range, + public readonly newText: string + ) { } + + public equals(other: RangeEdit): boolean { + return Range.equalsRange(this.range, other.range) && this.newText === other.newText; + } +} + export class LineEdits { - constructor(public readonly edits: readonly LineEdit[]) { } + constructor(public readonly edits: readonly LineRangeEdit[]) { } public apply(model: ITextModel): void { model.pushEditOperations( @@ -76,6 +86,10 @@ export class LineRange { return new LineRange(startLineNumber, endLineNumber - startLineNumber); } + static fromLineNumbers(startLineNumber: number, endExclusiveLineNumber: number): LineRange { + return new LineRange(startLineNumber, endExclusiveLineNumber - startLineNumber); + } + constructor( public readonly startLineNumber: number, public readonly lineCount: number @@ -131,6 +145,10 @@ export class LineRange { return new LineRange(this.startLineNumber, this.lineCount + delta); } + public deltaStart(lineDelta: number): LineRange { + return new LineRange(this.startLineNumber + lineDelta, this.lineCount - lineDelta); + } + public getLines(model: ITextModel): string[] { const result = new Array(this.lineCount); for (let i = 0; i < this.lineCount; i++) { @@ -138,119 +156,146 @@ export class LineRange { } return result; } -} -export class LineDiff { - public static fromLineChange(lineChange: ILineChange, originalTextModel: ITextModel, modifiedTextModel: ITextModel): LineDiff { - let originalRange: LineRange; - if (lineChange.originalEndLineNumber === 0) { - // Insertion - originalRange = new LineRange(lineChange.originalStartLineNumber + 1, 0); - } else { - originalRange = new LineRange(lineChange.originalStartLineNumber, lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1); - } - - let modifiedRange: LineRange; - if (lineChange.modifiedEndLineNumber === 0) { - // Insertion - modifiedRange = new LineRange(lineChange.modifiedStartLineNumber + 1, 0); - } else { - modifiedRange = new LineRange(lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1); - } - - return new LineDiff( - originalTextModel, - originalRange, - modifiedTextModel, - modifiedRange, - ); + public containsRange(range: LineRange): boolean { + return this.startLineNumber <= range.startLineNumber && range.endLineNumberExclusive <= this.endLineNumberExclusive; } - public static hull(lineDiffs: readonly LineDiff[]): LineDiff | undefined { + public toRange(): Range { + return new Range(this.startLineNumber, 1, this.endLineNumberExclusive, 1); + } +} + +export class LineRangeMapping { + public static hull(lineDiffs: readonly LineRangeMapping[]): LineRangeMapping | undefined { if (lineDiffs.length === 0) { return undefined; } - return new LineDiff( - lineDiffs[0].originalTextModel, - LineRange.join(lineDiffs.map((d) => d.originalRange))!, - lineDiffs[0].modifiedTextModel, - LineRange.join(lineDiffs.map((d) => d.modifiedRange))!, + return new LineRangeMapping( + lineDiffs[0].inputTextModel, + LineRange.join(lineDiffs.map((d) => d.inputRange))!, + lineDiffs[0].outputTextModel, + LineRange.join(lineDiffs.map((d) => d.outputRange))!, + [] ); } - public static alignOriginalRange(lineDiffs: readonly LineDiff[]): LineDiff[] { + public static alignOriginalRange(lineDiffs: readonly LineRangeMapping[]): LineRangeMapping[] { if (lineDiffs.length === 0) { return []; } - const originalRange = LineRange.join(lineDiffs.map((d) => d.originalRange))!; - return lineDiffs.map(l => { - const startDelta = originalRange.startLineNumber - l.originalRange.startLineNumber; - const endDelta = originalRange.endLineNumberExclusive - l.originalRange.endLineNumberExclusive; - return new LineDiff( - l.originalTextModel, - originalRange, - l.modifiedTextModel, - new LineRange( - l.modifiedRange.startLineNumber + startDelta, - l.modifiedRange.lineCount - startDelta + endDelta - ) - ); - }); + const originalRange = LineRange.join(lineDiffs.map((d) => d.inputRange))!; + return lineDiffs.map(l => l.extendInputRange(originalRange)); } + public readonly innerRangeMappings: readonly RangeMapping[]; + constructor( - public readonly originalTextModel: ITextModel, - public readonly originalRange: LineRange, - public readonly modifiedTextModel: ITextModel, - public readonly modifiedRange: LineRange, + public readonly inputTextModel: ITextModel, + public readonly inputRange: LineRange, + public readonly outputTextModel: ITextModel, + public readonly outputRange: LineRange, + innerRangeMappings?: readonly RangeMapping[], ) { + this.innerRangeMappings = innerRangeMappings + ? innerRangeMappings + : [ + new RangeMapping( + this.inputRange.toRange(), + this.outputRange.toRange() + ), + ]; + } + + public extendInputRange(extendedOriginalRange: LineRange): LineRangeMapping { + if (!extendedOriginalRange.containsRange(this.inputRange)) { + throw new BugIndicatingError(); + } + + const startDelta = extendedOriginalRange.startLineNumber - this.inputRange.startLineNumber; + const endDelta = extendedOriginalRange.endLineNumberExclusive - this.inputRange.endLineNumberExclusive; + return new LineRangeMapping( + this.inputTextModel, + extendedOriginalRange, + this.outputTextModel, + new LineRange( + this.outputRange.startLineNumber + startDelta, + this.outputRange.lineCount - startDelta + endDelta + ), + this.innerRangeMappings, + ); } public get resultingDeltaFromOriginalToModified(): number { - return this.modifiedRange.endLineNumberExclusive - this.originalRange.endLineNumberExclusive; + return this.outputRange.endLineNumberExclusive - this.inputRange.endLineNumberExclusive; } - private ensureSameOriginalModel(other: LineDiff): void { - if (this.originalTextModel !== other.originalTextModel) { + private ensureSameInputModel(other: LineRangeMapping): void { + if (this.inputTextModel !== other.inputTextModel) { // Both changes must refer to the same original model throw new BugIndicatingError(); } } - public conflicts(other: LineDiff): boolean { - this.ensureSameOriginalModel(other); - return this.originalRange.touches(other.originalRange); + public isStrictBefore(other: LineRangeMapping): boolean { + this.ensureSameInputModel(other); + return this.inputRange.endLineNumberExclusive <= other.inputRange.startLineNumber; } - public isStrictBefore(other: LineDiff): boolean { - this.ensureSameOriginalModel(other); - return this.originalRange.endLineNumberExclusive <= other.originalRange.startLineNumber; - } - - public getLineEdit(): LineEdit { - return new LineEdit( - this.originalRange, - this.getModifiedLines() + public getLineEdit(): LineRangeEdit { + return new LineRangeEdit( + this.inputRange, + this.getOutputLines() ); } - public getReverseLineEdit(): LineEdit { - return new LineEdit( - this.modifiedRange, - this.getOriginalLines() + public getReverseLineEdit(): LineRangeEdit { + return new LineRangeEdit( + this.outputRange, + this.getInputLines() ); } - private getModifiedLines(): string[] { - return this.modifiedRange.getLines(this.modifiedTextModel); + private getOutputLines(): string[] { + return this.outputRange.getLines(this.outputTextModel); } - private getOriginalLines(): string[] { - return this.originalRange.getLines(this.originalTextModel); + private getInputLines(): string[] { + return this.inputRange.getLines(this.inputTextModel); + } + + public addOutputLineDelta(delta: number): LineRangeMapping { + return new LineRangeMapping( + this.inputTextModel, + this.inputRange, + this.outputTextModel, + this.outputRange.delta(delta), + this.innerRangeMappings.map(d => d.addOutputLineDelta(delta)) + ); } } +export class RangeMapping { + constructor(public readonly inputRange: Range, public readonly outputRange: Range) { + } + + toString(): string { + return `${this.inputRange.toString()} -> ${this.outputRange.toString()}`; + } + + addOutputLineDelta(deltaLines: number): RangeMapping { + return new RangeMapping( + this.inputRange, + new Range( + this.outputRange.startLineNumber + deltaLines, + this.outputRange.startColumn, + this.outputRange.endLineNumber + deltaLines, + this.outputRange.endColumn + ) + ); + } +} /** * Describes modifications in input 1 and input 2 for a specific range in base. @@ -267,12 +312,12 @@ export class ModifiedBaseRange { public static fromDiffs( baseTextModel: ITextModel, input1TextModel: ITextModel, - diffs1: readonly LineDiff[], + diffs1: readonly LineRangeMapping[], input2TextModel: ITextModel, - diffs2: readonly LineDiff[] + diffs2: readonly LineRangeMapping[] ): ModifiedBaseRange[] { - const compareByStartLineNumber = compareBy( - (d) => d.originalRange.startLineNumber, + const compareByStartLineNumber = compareBy( + (d) => d.inputRange.startLineNumber, numberComparator ); @@ -283,8 +328,8 @@ export class ModifiedBaseRange { diffs.sort(compareBy(d => d.diff, compareByStartLineNumber)); const currentDiffs = [ - new Array(), - new Array(), + new Array(), + new Array(), ]; const deltaFromBaseToInput = [0, 0]; @@ -310,9 +355,10 @@ export class ModifiedBaseRange { let currentRange: LineRange | undefined; for (const diff of diffs) { - const range = diff.diff.originalRange; + const range = diff.diff.inputRange; if (currentRange && !currentRange.touches(range)) { pushAndReset(); + currentRange = undefined; } deltaFromBaseToInput[diff.source] = diff.diff.resultingDeltaFromOriginalToModified; currentRange = currentRange ? currentRange.join(range) : range; @@ -323,8 +369,8 @@ export class ModifiedBaseRange { return result; } - public readonly input1CombinedDiff = LineDiff.hull(this.input1Diffs); - public readonly input2CombinedDiff = LineDiff.hull(this.input2Diffs); + public readonly input1CombinedDiff = LineRangeMapping.hull(this.input1Diffs); + public readonly input2CombinedDiff = LineRangeMapping.hull(this.input2Diffs); public readonly baseRange: LineRange; public readonly input1Range: LineRange; @@ -333,10 +379,10 @@ export class ModifiedBaseRange { constructor( public readonly baseTextModel: ITextModel, public readonly input1TextModel: ITextModel, - public readonly input1Diffs: readonly LineDiff[], + public readonly input1Diffs: readonly LineRangeMapping[], input1DeltaLineCount: number, public readonly input2TextModel: ITextModel, - public readonly input2Diffs: readonly LineDiff[], + public readonly input2Diffs: readonly LineRangeMapping[], input2DeltaLineCount: number, ) { if (this.input1Diffs.length === 0 && this.input2Diffs.length === 0) { @@ -345,33 +391,35 @@ export class ModifiedBaseRange { const input1Diff = this.input1CombinedDiff || - new LineDiff( + new LineRangeMapping( baseTextModel, - this.input2CombinedDiff!.originalRange, + this.input2CombinedDiff!.inputRange, input1TextModel, - this.input2CombinedDiff!.originalRange.delta(input1DeltaLineCount) + this.input2CombinedDiff!.inputRange.delta(input1DeltaLineCount), + [] ); const input2Diff = this.input2CombinedDiff || - new LineDiff( + new LineRangeMapping( baseTextModel, - this.input1CombinedDiff!.originalRange, + this.input1CombinedDiff!.inputRange, input1TextModel, - this.input1CombinedDiff!.originalRange.delta(input2DeltaLineCount) + this.input1CombinedDiff!.inputRange.delta(input2DeltaLineCount), + [] ); - const results = LineDiff.alignOriginalRange([input1Diff, input2Diff]); - this.baseRange = results[0].originalRange; - this.input1Range = results[0].modifiedRange; - this.input2Range = results[1].modifiedRange; + const results = LineRangeMapping.alignOriginalRange([input1Diff, input2Diff]); + this.baseRange = results[0].inputRange; + this.input1Range = results[0].outputRange; + this.input2Range = results[1].outputRange; } public getInputRange(inputNumber: 1 | 2): LineRange { return inputNumber === 1 ? this.input1Range : this.input2Range; } - public getInputDiffs(inputNumber: 1 | 2): readonly LineDiff[] { + public getInputDiffs(inputNumber: 1 | 2): readonly LineRangeMapping[] { return inputNumber === 1 ? this.input1Diffs : this.input2Diffs; } @@ -391,14 +439,14 @@ export class ModifiedBaseRangeState { public readonly conflicting: boolean, ) { } - public getInput(inputNumber: 1 | 2): boolean | undefined { + public getInput(inputNumber: 1 | 2): ToggleState { if (this.conflicting) { - return undefined; + return ToggleState.conflicting; } if (inputNumber === 1) { - return this.input1; + return !this.input1 ? ToggleState.unset : this.input2First ? ToggleState.second : ToggleState.first; } else { - return this.input2; + return !this.input2 ? ToggleState.unset : !this.input2First ? ToggleState.second : ToggleState.first; } } @@ -451,15 +499,21 @@ export class ModifiedBaseRangeState { } } -/* -export class LineMappings { +export const enum ToggleState { + unset = 0, + first = 1, + second = 2, + conflicting = 3, +} + +export class DocumentMapping { public static fromDiffs( - diffs1: readonly LineDiff[], - diffs2: readonly LineDiff[], - inputLineCount: number, - ): LineMappings { - const compareByStartLineNumber = compareBy( - (d) => d.originalRange.startLineNumber, + diffs1: readonly LineRangeMapping[], + diffs2: readonly LineRangeMapping[], + inputLineCount: number + ): DocumentMapping { + const compareByStartLineNumber = compareBy( + (d) => d.inputRange.startLineNumber, numberComparator ); @@ -467,58 +521,100 @@ export class LineMappings { .map((diff) => ({ source: 0 as 0 | 1, diff })) .concat(diffs2.map((diff) => ({ source: 1 as const, diff }))); - diffs.sort(compareBy(d => d.diff, compareByStartLineNumber)); + diffs.sort(compareBy((d) => d.diff, compareByStartLineNumber)); - const currentDiffs = [ - new Array(), - new Array(), - ]; - let deltaFromBaseToInput = [0, 0]; + const currentDiffs = [new Array(), new Array()]; + const deltaFromBaseToInput = [0, 0]; - const result = new Array(); + const result = new Array(); - function pushAndReset() { - result.push(LineMapping.create( - baseTextModel, - input1TextModel, - currentDiffs[0], - deltaFromBaseToInput[0], - input2TextModel, - currentDiffs[1], - deltaFromBaseToInput[1], - )); + function pushAndReset(baseRange: LineRange) { + const input1Range = LineRange.join(currentDiffs[0].map(d => d.outputRange)) || baseRange.delta(deltaFromBaseToInput[0]); + const input1BaseRange = LineRange.join(currentDiffs[0].map(d => d.inputRange)) || baseRange; + const mapping1 = new SimpleLineRangeMapping(input1BaseRange, input1Range); + + const input2Range = LineRange.join(currentDiffs[1].map(d => d.outputRange)) || baseRange.delta(deltaFromBaseToInput[1]); + const input2BaseRange = LineRange.join(currentDiffs[1].map(d => d.inputRange)) || baseRange; + const mapping2 = new SimpleLineRangeMapping(input2BaseRange, input2Range); + + result.push( + new SimpleLineRangeMapping( + mapping1.extendInputRange(currentInputRange!).outputRange, + mapping2.extendInputRange(currentInputRange!).outputRange + ) + ); currentDiffs[0] = []; currentDiffs[1] = []; } - let currentRange: LineRange | undefined; + let currentInputRange: LineRange | undefined; for (const diff of diffs) { - const range = diff.diff.originalRange; - if (currentRange && !currentRange.touches(range)) { - pushAndReset(); + const range = diff.diff.inputRange; + if (currentInputRange && !currentInputRange.touches(range)) { + pushAndReset(currentInputRange); + currentInputRange = undefined; } - deltaFromBaseToInput[diff.source] = diff.diff.resultingDeltaFromOriginalToModified; - currentRange = currentRange ? currentRange.join(range) : range; + deltaFromBaseToInput[diff.source] = + diff.diff.resultingDeltaFromOriginalToModified; + currentInputRange = currentInputRange ? currentInputRange.join(range) : range; currentDiffs[diff.source].push(diff.diff); } - pushAndReset(); + if (currentInputRange) { + pushAndReset(currentInputRange); + } - return result; + return new DocumentMapping(result, inputLineCount); } - constructor(private readonly lineMappings: LineMapping[]) {} -} - -// A lightweight ModifiedBaseRange. Maybe they can be united? -export class LineMapping { - public static create(input: LineDiff, ): LineMapping { + public getOutputLine(inputLineNumber: number): number | SimpleLineRangeMapping { + const lastBefore = findLast(this.lineRangeMappings, r => r.inputRange.startLineNumber <= inputLineNumber); + if (lastBefore) { + if (lastBefore.inputRange.contains(inputLineNumber)) { + return lastBefore; + } + return inputLineNumber + lastBefore.outputRange.endLineNumberExclusive - lastBefore.inputRange.endLineNumberExclusive; + } + return inputLineNumber; + } + public getInputLine(outputLineNumber: number): number | SimpleLineRangeMapping { + const lastBefore = findLast(this.lineRangeMappings, r => r.outputRange.startLineNumber <= outputLineNumber); + if (lastBefore) { + if (lastBefore.outputRange.contains(outputLineNumber)) { + return lastBefore; + } + return outputLineNumber + lastBefore.inputRange.endLineNumberExclusive - lastBefore.outputRange.endLineNumberExclusive; + } + return outputLineNumber; } constructor( - public readonly inputRange: LineRange, - public readonly resultRange: LineRange + public readonly lineRangeMappings: SimpleLineRangeMapping[], + public readonly inputLineCount: number ) { } } -*/ + +export class SimpleLineRangeMapping { + constructor( + public readonly inputRange: LineRange, + public readonly outputRange: LineRange + ) { } + + public extendInputRange(extendedInputRange: LineRange): SimpleLineRangeMapping { + if (!extendedInputRange.containsRange(this.inputRange)) { + throw new BugIndicatingError(); + } + + const startDelta = extendedInputRange.startLineNumber - this.inputRange.startLineNumber; + const endDelta = extendedInputRange.endLineNumberExclusive - this.inputRange.endLineNumberExclusive; + return new SimpleLineRangeMapping( + extendedInputRange, + new LineRange( + this.outputRange.startLineNumber + startDelta, + this.outputRange.lineCount - startDelta + endDelta + ) + ); + } +} + diff --git a/src/vs/workbench/contrib/mergeEditor/browser/textModelDiffs.ts b/src/vs/workbench/contrib/mergeEditor/browser/textModelDiffs.ts index e7c6c9967e5..cce7137adf1 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/textModelDiffs.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/textModelDiffs.ts @@ -7,15 +7,15 @@ import { compareBy, numberComparator } from 'vs/base/common/arrays'; import { BugIndicatingError } from 'vs/base/common/errors'; import { Disposable } from 'vs/base/common/lifecycle'; import { ITextModel } from 'vs/editor/common/model'; -import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { IObservable, ITransaction, ObservableValue, transaction } from 'vs/workbench/contrib/audioCues/browser/observable'; -import { LineDiff, LineEdit, LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model'; +import { LineRangeEdit, LineRange, LineRangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model'; import { ReentrancyBarrier } from 'vs/workbench/contrib/mergeEditor/browser/utils'; +import { IDiffComputer } from './diffComputer'; export class TextModelDiffs extends Disposable { private updateCount = 0; private readonly _state = new ObservableValue(TextModelDiffState.initializing, 'LiveDiffState'); - private readonly _diffs = new ObservableValue([], 'LiveDiffs'); + private readonly _diffs = new ObservableValue([], 'LiveDiffs'); private readonly barrier = new ReentrancyBarrier(); @@ -35,7 +35,7 @@ export class TextModelDiffs extends Disposable { return this._state; } - public get diffs(): IObservable { + public get diffs(): IObservable { return this._diffs; } @@ -63,9 +63,9 @@ export class TextModelDiffs extends Disposable { } transaction(tx => { - if (result) { + if (result.diffs) { this._state.set(TextModelDiffState.upToDate, tx, TextModelDiffChangeReason.textChange); - this._diffs.set(result, tx, TextModelDiffChangeReason.textChange); + this._diffs.set(result.diffs, tx, TextModelDiffChangeReason.textChange); } else { this._state.set(TextModelDiffState.error, tx, TextModelDiffChangeReason.textChange); } @@ -78,10 +78,10 @@ export class TextModelDiffs extends Disposable { } } - public removeDiffs(diffToRemoves: LineDiff[], transaction: ITransaction | undefined): void { + public removeDiffs(diffToRemoves: LineRangeMapping[], transaction: ITransaction | undefined): void { this.ensureUpToDate(); - diffToRemoves.sort(compareBy((d) => d.originalRange.startLineNumber, numberComparator)); + diffToRemoves.sort(compareBy((d) => d.inputRange.startLineNumber, numberComparator)); diffToRemoves.reverse(); let diffs = this._diffs.get(); @@ -99,15 +99,8 @@ export class TextModelDiffs extends Disposable { }); diffs = diffs.map((d) => - d.modifiedRange.isAfter(diffToRemove.modifiedRange) - ? new LineDiff( - d.originalTextModel, - d.originalRange, - d.modifiedTextModel, - d.modifiedRange.delta( - diffToRemove.originalRange.lineCount - diffToRemove.modifiedRange.lineCount - ) - ) + d.outputRange.isAfter(diffToRemove.outputRange) + ? d.addOutputLineDelta(diffToRemove.inputRange.lineCount - diffToRemove.outputRange.lineCount) : d ); } @@ -118,80 +111,79 @@ export class TextModelDiffs extends Disposable { /** * Edit must be conflict free. */ - public applyEditRelativeToOriginal(edit: LineEdit, transaction: ITransaction | undefined): void { + public applyEditRelativeToOriginal(edit: LineRangeEdit, transaction: ITransaction | undefined): void { this.ensureUpToDate(); + const editMapping = new LineRangeMapping( + this.baseTextModel, + edit.range, + this.textModel, + new LineRange(edit.range.startLineNumber, edit.newLines.length) + ); + let firstAfter = false; let delta = 0; - const newDiffs = new Array(); + const newDiffs = new Array(); for (const diff of this.diffs.get()) { - if (diff.originalRange.touches(edit.range)) { + if (diff.inputRange.touches(edit.range)) { throw new BugIndicatingError('Edit must be conflict free.'); - } else if (diff.originalRange.isAfter(edit.range)) { + } else if (diff.inputRange.isAfter(edit.range)) { if (!firstAfter) { firstAfter = true; - - newDiffs.push(new LineDiff( - this.baseTextModel, - edit.range, - this.textModel, - new LineRange(edit.range.startLineNumber + delta, edit.newLines.length) - )); + newDiffs.push(editMapping.addOutputLineDelta(delta)); } - newDiffs.push(new LineDiff( - diff.originalTextModel, - diff.originalRange, - diff.modifiedTextModel, - diff.modifiedRange.delta(edit.newLines.length - edit.range.lineCount) - )); + newDiffs.push(diff.addOutputLineDelta(edit.newLines.length - edit.range.lineCount)); } else { newDiffs.push(diff); } if (!firstAfter) { - delta += diff.modifiedRange.lineCount - diff.originalRange.lineCount; + delta += diff.outputRange.lineCount - diff.inputRange.lineCount; } } if (!firstAfter) { firstAfter = true; - - newDiffs.push(new LineDiff( - this.baseTextModel, - edit.range, - this.textModel, - new LineRange(edit.range.startLineNumber + delta, edit.newLines.length) - )); + newDiffs.push(editMapping.addOutputLineDelta(delta)); } this.barrier.runExclusivelyOrThrow(() => { - new LineEdit(edit.range.delta(delta), edit.newLines).apply(this.textModel); + new LineRangeEdit(edit.range.delta(delta), edit.newLines).apply(this.textModel); }); this._diffs.set(newDiffs, transaction, TextModelDiffChangeReason.other); } - public findTouchingDiffs(baseRange: LineRange): LineDiff[] { - return this.diffs.get().filter(d => d.originalRange.touches(baseRange)); + public findTouchingDiffs(baseRange: LineRange): LineRangeMapping[] { + return this.diffs.get().filter(d => d.inputRange.touches(baseRange)); } - /* - public getResultRange(baseRange: LineRange): LineRange { - let startOffset = 0; - let lengthOffset = 0; + private getResultLine(lineNumber: number): number | LineRangeMapping { + let offset = 0; for (const diff of this.diffs.get()) { - if (diff.originalRange.endLineNumberExclusive <= baseRange.startLineNumber) { - startOffset += diff.resultingDeltaFromOriginalToModified; - } else if (diff.originalRange.startLineNumber <= baseRange.endLineNumberExclusive) { - lengthOffset += diff.resultingDeltaFromOriginalToModified; + if (diff.inputRange.contains(lineNumber) || diff.inputRange.endLineNumberExclusive === lineNumber) { + return diff; + } else if (diff.inputRange.endLineNumberExclusive < lineNumber) { + offset = diff.resultingDeltaFromOriginalToModified; } else { break; } } - - return new LineRange(baseRange.startLineNumber + startOffset, baseRange.lineCount + lengthOffset); + return lineNumber + offset; + } + + public getResultRange(baseRange: LineRange): LineRange { + let start = this.getResultLine(baseRange.startLineNumber); + if (typeof start !== 'number') { + start = start.outputRange.startLineNumber; + } + let endExclusive = this.getResultLine(baseRange.endLineNumberExclusive); + if (typeof endExclusive !== 'number') { + endExclusive = endExclusive.outputRange.endLineNumberExclusive; + } + + return LineRange.fromLineNumbers(start, endExclusive); } - */ } export const enum TextModelDiffChangeReason { @@ -208,22 +200,5 @@ export const enum TextModelDiffState { export interface ITextModelDiffsState { state: TextModelDiffState; - diffs: LineDiff[]; -} - -export interface IDiffComputer { - computeDiff(textModel1: ITextModel, textModel2: ITextModel): Promise; -} - -export class EditorWorkerServiceDiffComputer implements IDiffComputer { - constructor(@IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService) { } - - async computeDiff(textModel1: ITextModel, textModel2: ITextModel): Promise { - //await wait(1000); - const diffs = await this.editorWorkerService.computeDiff(textModel1.uri, textModel2.uri, false, 1000); - if (!diffs || diffs.quitEarly) { - return null; - } - return diffs.changes.map((c) => LineDiff.fromLineChange(c, textModel1, textModel2)); - } + diffs: LineRangeMapping[]; } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/utils.ts b/src/vs/workbench/contrib/mergeEditor/browser/utils.ts index d72b2dff331..f6859c527e8 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/utils.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/utils.ts @@ -54,14 +54,14 @@ export class ReentrancyBarrier { } export function n(tag: TTag): never; +export function n( + tag: TTag, + attributes: { $: TId } +): Record>; export function n)[]>( tag: TTag, children: T ): (ArrayToObj & Record<'root', TagToElement>) extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never; -export function n( - tag: TTag, - attributes: { $: TId } -): Record>; export function n)[]>( tag: TTag, attributes: { $: TId }, @@ -199,3 +199,11 @@ export function* join( yield { left: leftElement, rights: equals || [] }; } } + +export function concatArrays(...arrays: TArr): TArr[number][number][] { + return ([] as any[]).concat(...arrays); +} + +export function elementAtOrUndefined(arr: T[], index: number): T | undefined { + return arr[index]; +} From 88c01e48252ea48cd66d5b7d078fbf0788a86c39 Mon Sep 17 00:00:00 2001 From: Robo Date: Thu, 16 Jun 2022 06:32:57 +0900 Subject: [PATCH 069/175] chore: bump electron@18.3.3 (#152236) --- .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 c6a9765f61f..be7ab934758 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,4 +1,4 @@ disturl "https://electronjs.org/headers" -target "18.3.2" +target "18.3.3" runtime "electron" build_from_source "true" diff --git a/cgmanifest.json b/cgmanifest.json index 59dc01361ce..5d239f2547d 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -60,12 +60,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "6839dd92b84057e79ddce06f8291cc56a0a51ff6" + "commitHash": "15f3c45fe9292fb80c5b5c4d26a218e825287d07" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "18.3.2" + "version": "18.3.3" }, { "component": { diff --git a/package.json b/package.json index 96316bdfb26..6db4d010819 100644 --- a/package.json +++ b/package.json @@ -138,7 +138,7 @@ "cssnano": "^4.1.11", "debounce": "^1.0.0", "deemon": "^1.4.0", - "electron": "18.3.2", + "electron": "18.3.3", "eslint": "8.7.0", "eslint-plugin-header": "3.1.1", "eslint-plugin-jsdoc": "^39.3.2", diff --git a/yarn.lock b/yarn.lock index 1e613966025..31a0f3d05df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4335,10 +4335,10 @@ electron-to-chromium@^1.4.17: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.45.tgz#cf1144091d6683cbd45a231954a745f02fb24598" integrity sha512-czF9eYVuOmlY/vxyMQz2rGlNSjZpxNQYBe1gmQv7al171qOIhgyO9k7D5AKlgeTCSPKk+LHhj5ZyIdmEub9oNg== -electron@18.3.2: - version "18.3.2" - resolved "https://registry.yarnpkg.com/electron/-/electron-18.3.2.tgz#015a8f4c92c62855d7f33206f2166d3e33b053b7" - integrity sha512-Q1ciZ1M90L71WvyLbkD8Iwaq4YCwo8NUpBiLQUsd6M4E7i5vrzsA4g5Ylfzyela8DgRCNVknDVDfj6s+7YVWpA== +electron@18.3.3: + version "18.3.3" + resolved "https://registry.yarnpkg.com/electron/-/electron-18.3.3.tgz#1c48273c1ad1522b8c18f19575e862c7ccd9f409" + integrity sha512-LYxf3uCDc/r0klu7LL0eZLxkseoGIY/vrCfS0Qj4YTU3M7LLjOaIqrajI7icKwaI2dgxiuJJH3n4eqALFpJAFg== dependencies: "@electron/get" "^1.13.0" "@types/node" "^16.11.26" From 8fb977f2c1b783698e9595e5dd99599efccefb92 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 15 Jun 2022 14:41:52 -0700 Subject: [PATCH 070/175] Make `jsx: react` the default for implicit js/ts projects (#152256) Make jsx: react the default Fixes #152150 This fixes imports of `react` getting removed with TS 4.7 in implicit projects --- extensions/typescript-language-features/src/utils/tsconfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/utils/tsconfig.ts b/extensions/typescript-language-features/src/utils/tsconfig.ts index b92a26916d1..baf1d4fd59b 100644 --- a/extensions/typescript-language-features/src/utils/tsconfig.ts +++ b/extensions/typescript-language-features/src/utils/tsconfig.ts @@ -26,7 +26,7 @@ const defaultProjectConfig = Object.freeze module: 'ESNext' as Proto.ModuleKind, moduleResolution: 'Node' as Proto.ModuleResolutionKind, target: 'ES2020' as Proto.ScriptTarget, - jsx: 'preserve' as Proto.JsxEmit, + jsx: 'react' as Proto.JsxEmit, }); export function inferredProjectCompilerOptions( From a289e64e096cbfb1c9f77e40618856dd029a7de2 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 15 Jun 2022 15:37:21 -0700 Subject: [PATCH 071/175] Switch to finalized TS protocol types (#152259) --- .../src/languageFeatures/sourceDefinition.ts | 32 ++----------------- .../src/typescriptService.ts | 1 + 2 files changed, 3 insertions(+), 30 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/sourceDefinition.ts b/extensions/typescript-language-features/src/languageFeatures/sourceDefinition.ts index d020d265995..527a25ef8b2 100644 --- a/extensions/typescript-language-features/src/languageFeatures/sourceDefinition.ts +++ b/extensions/typescript-language-features/src/languageFeatures/sourceDefinition.ts @@ -6,41 +6,13 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import { Command, CommandManager } from '../commands/commandManager'; -import * as Proto from '../protocol'; -import { ExecConfig, ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; +import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { isSupportedLanguageMode } from '../utils/languageIds'; import * as typeConverters from '../utils/typeConverters'; const localize = nls.loadMessageBundle(); -namespace ExperimentalProto { - export const enum CommandTypes { - FindSourceDefinition = 'findSourceDefinition' - } - - export interface SourceDefinitionRequestArgs extends Proto.FileLocationRequestArgs { } - - export interface SourceDefinitionRequest extends Proto.Request { - command: CommandTypes.FindSourceDefinition; - arguments: SourceDefinitionRequestArgs; - } - - export interface InlayHintsResponse extends Proto.DefinitionResponse { } - - export interface IExtendedTypeScriptServiceClient { - execute( - command: K, - args: ExtendedTsServerRequests[K][0], - token: vscode.CancellationToken, - config?: ExecConfig - ): Promise>; - } - - export interface ExtendedTsServerRequests { - 'findSourceDefinition': [SourceDefinitionRequestArgs, InlayHintsResponse]; - } -} class SourceDefinitionCommand implements Command { @@ -85,7 +57,7 @@ class SourceDefinitionCommand implements Command { const position = activeEditor.selection.anchor; const args = typeConverters.Position.toFileLocationRequestArgs(openedFiledPath, position); - const response = await (this.client as ExperimentalProto.IExtendedTypeScriptServiceClient).execute('findSourceDefinition', args, token); + const response = await this.client.execute('findSourceDefinition', args, token); if (response.type === 'response' && response.body) { const locations: vscode.Location[] = response.body.map(reference => typeConverters.Location.fromTextSpan(this.client.toResource(reference.file), reference)); diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index 616131b26d8..681cee3a4b5 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -73,6 +73,7 @@ interface StandardTsServerRequests { 'fileReferences': [Proto.FileRequestArgs, Proto.FileReferencesResponse]; 'provideInlayHints': [Proto.InlayHintsRequestArgs, Proto.InlayHintsResponse]; 'encodedSemanticClassifications-full': [Proto.EncodedSemanticClassificationsRequestArgs, Proto.EncodedSemanticClassificationsResponse]; + 'findSourceDefinition': [Proto.FileLocationRequestArgs, Proto.DefinitionResponse]; } interface NoResponseTsServerRequests { From a765abcf07ed14495197fc39de3ae418f823d340 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 15 Jun 2022 15:43:09 -0700 Subject: [PATCH 072/175] Adopt ResourceMap for MD diagnostics (#152264) Adopt ResourceMap Switches to use `ResourceMap` instead of our own implementation --- .../src/languageFeatures/diagnostics.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts index c7237493eca..6a76b4614f3 100644 --- a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts @@ -136,7 +136,7 @@ class LinkWatcher extends Disposable { */ public readonly onDidChangeLinkedToFile = this._onDidChangeLinkedToFile.event; - private readonly _watchers = new Map; + readonly documents: ResourceMap; }>(); override dispose() { @@ -168,21 +168,21 @@ class LinkWatcher extends Disposable { // First decrement watcher counter for previous document state for (const entry of this._watchers.values()) { - entry.documents.delete(document.toString()); + entry.documents.delete(document); } // Then create/update watchers for new document state for (const path of linkedToResource) { - let entry = this._watchers.get(path.toString()); + let entry = this._watchers.get(path); if (!entry) { entry = { watcher: this.startWatching(path), - documents: new Map(), + documents: new ResourceMap(), }; - this._watchers.set(path.toString(), entry); + this._watchers.set(path, entry); } - entry.documents.set(document.toString(), document); + entry.documents.set(document, document); } // Finally clean up watchers for links that are no longer are referenced anywhere @@ -209,7 +209,7 @@ class LinkWatcher extends Disposable { } private onLinkedResourceChanged(resource: vscode.Uri) { - const entry = this._watchers.get(resource.toString()); + const entry = this._watchers.get(resource); if (entry) { this._onDidChangeLinkedToFile.fire(entry.documents.values()); } From d466785b7cd74cd397249523c7017ba54d5e4e6b Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 15 Jun 2022 15:56:32 -0700 Subject: [PATCH 073/175] Allow requestUsbDevice to take filters and return device data (#152257) --- .../browser/actions/workspaceCommands.ts | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/actions/workspaceCommands.ts b/src/vs/workbench/browser/actions/workspaceCommands.ts index dd3fb125fb5..f293f39d156 100644 --- a/src/vs/workbench/browser/actions/workspaceCommands.ts +++ b/src/vs/workbench/browser/actions/workspaceCommands.ts @@ -309,7 +309,42 @@ CommandsRegistry.registerCommand('_workbench.getRecentlyOpened', async function }); if (isWeb) { - CommandsRegistry.registerCommand('workbench.experimental.requestUsbDevice', async (_accessor: ServicesAccessor): Promise => { - await (navigator as any).usb.requestDevice({ filters: [] }); + interface UsbDeviceData { + readonly deviceClass: number; + readonly deviceProtocol: number; + readonly deviceSubclass: number; + readonly deviceVersionMajor: number; + readonly deviceVersionMinor: number; + readonly deviceVersionSubminor: number; + readonly manufacturerName?: string; + readonly productId: number; + readonly productName?: string; + readonly serialNumber?: string; + readonly usbVersionMajor: number; + readonly usbVersionMinor: number; + readonly usbVersionSubminor: number; + readonly vendorId: number; + } + CommandsRegistry.registerCommand('workbench.experimental.requestUsbDevice', async (_accessor: ServicesAccessor, options?: { filters?: unknown[] }): Promise => { + const device = await (navigator as any).usb.requestDevice({ filters: options?.filters ?? [] }); + if (!device) { + return undefined; + } + return { + deviceClass: device.deviceClass, + deviceProtocol: device.deviceProtocol, + deviceSubclass: device.deviceSubclass, + deviceVersionMajor: device.deviceVersionMajor, + deviceVersionMinor: device.deviceVersionMinor, + deviceVersionSubminor: device.deviceVersionSubminor, + manufacturerName: device.manufacturerName, + productId: device.productId, + productName: device.productName, + serialNumber: device.serialNumber, + usbVersionMajor: device.usbVersionMajor, + usbVersionMinor: device.usbVersionMinor, + usbVersionSubminor: device.usbVersionSubminor, + vendorId: device.vendorId, + }; }); } From f051310f00598c60e5db6f782824463acecde534 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 15 Jun 2022 16:30:12 -0700 Subject: [PATCH 074/175] updates for windows --- src/vs/platform/native/common/native.ts | 2 +- .../native/electron-main/nativeHostMainService.ts | 7 ++++--- .../browser/parts/titlebar/media/titlebarpart.css | 12 +++++++++--- .../parts/titlebar/titlebarPart.ts | 15 ++++++++++++++- .../electron-browser/workbenchTestServices.ts | 2 +- 5 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/vs/platform/native/common/native.ts b/src/vs/platform/native/common/native.ts index c5cfd92a41c..5f6937e8655 100644 --- a/src/vs/platform/native/common/native.ts +++ b/src/vs/platform/native/common/native.ts @@ -71,7 +71,7 @@ export interface ICommonNativeHostService { unmaximizeWindow(): Promise; minimizeWindow(): Promise; - updateTitleBarOverlay(backgroundColor: string, foregroundColor: string): Promise; + updateTitleBarOverlay(options: { height?: number; backgroundColor?: string; foregroundColor?: string }): Promise; setMinimumSize(width: number | undefined, height: number | undefined): Promise; diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index f9fdc695940..639c6cfa063 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -212,12 +212,13 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } } - async updateTitleBarOverlay(windowId: number | undefined, backgroundColor: string, foregroundColor: string): Promise { + async updateTitleBarOverlay(windowId: number | undefined, options: { height?: number; backgroundColor?: string; foregroundColor?: string }): Promise { const window = this.windowById(windowId); if (window?.win) { window.win.setTitleBarOverlay({ - color: backgroundColor, - symbolColor: foregroundColor + color: options.backgroundColor, + symbolColor: options.foregroundColor, + height: options.height ? options.height - 1 : undefined // account for window border }); } } diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 0df287925b9..d68e9da7536 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -228,7 +228,7 @@ text-align: center; z-index: 3000; -webkit-app-region: no-drag; - height: 30px; + height: 100%; width: 138px; zoom: calc(1 / var(--zoom-factor)); } @@ -246,13 +246,19 @@ /* Window Control Icons */ .monaco-workbench .part.titlebar>.window-controls-container>.window-icon { - display: inline-block; - line-height: 30px; + display: flex; + justify-content: center; + align-items: center; height: 100%; width: 46px; font-size: 16px; } +.monaco-workbench .part.titlebar>.window-controls-container>.window-icon::before { + height: 16px; + line-height: 16px; +} + .monaco-workbench .part.titlebar>.window-controls-container>.window-icon:hover { background-color: rgba(255, 255, 255, 0.1); } diff --git a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts index 6825771b6de..9d0e9a76570 100644 --- a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts @@ -27,6 +27,7 @@ export class TitlebarPart extends BrowserTitleBarPart { private dragRegion: HTMLElement | undefined; private resizer: HTMLElement | undefined; private cachedWindowControlStyles: { bgColor: string; fgColor: string } | undefined; + private cachedWindowControlHeight: number | undefined; private getMacTitlebarSize() { const osVersion = this.environmentService.os.release; @@ -205,7 +206,19 @@ export class TitlebarPart extends BrowserTitleBarPart { if (!this.cachedWindowControlStyles || this.cachedWindowControlStyles.bgColor !== this.element.style.backgroundColor || this.cachedWindowControlStyles.fgColor !== this.element.style.color) { - this.nativeHostService.updateTitleBarOverlay(this.element.style.backgroundColor, this.element.style.color); + this.nativeHostService.updateTitleBarOverlay({ backgroundColor: this.element.style.backgroundColor, foregroundColor: this.element.style.color }); + } + } + } + + override layout(width: number, height: number): void { + super.layout(width, height); + + if (useWindowControlsOverlay(this.configurationService, this.environmentService)) { + const newHeight = Math.trunc(this.element.clientHeight * getZoomFactor()); + if (newHeight !== this.cachedWindowControlHeight) { + this.cachedWindowControlHeight = newHeight; + this.nativeHostService.updateTitleBarOverlay({ height: newHeight }); } } } diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index 79d2fefb506..4346be73d8d 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -221,7 +221,7 @@ export class TestNativeHostService implements INativeHostService { async maximizeWindow(): Promise { } async unmaximizeWindow(): Promise { } async minimizeWindow(): Promise { } - async updateTitleBarOverlay(backgroundColor: string, foregroundColor: string): Promise { } + async updateTitleBarOverlay(options: { height?: number; backgroundColor?: string; foregroundColor?: string }): Promise { } async setMinimumSize(width: number | undefined, height: number | undefined): Promise { } async saveWindowSplash(value: IPartsSplash): Promise { } async focusWindow(options?: { windowId?: number | undefined } | undefined): Promise { } From 16c2a3ab3be4cf89c71fcce1ff1c11a06fcb923a Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Wed, 15 Jun 2022 16:59:20 -0800 Subject: [PATCH 075/175] =?UTF-8?q?revert=20quickpick=20changes,=20get=20t?= =?UTF-8?q?o=20work=20for=20build=20task,=20and=20address=20fee=E2=80=A6?= =?UTF-8?q?=20(#152106)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workbench/api/browser/mainThreadTask.ts | 9 +- src/vs/workbench/api/common/shared/tasks.ts | 2 - .../contrib/tasks/browser/taskQuickPick.ts | 89 +++---------------- .../tasks/browser/terminalTaskSystem.ts | 13 +-- .../contrib/tasks/common/jsonSchema_v2.ts | 35 +++++--- .../contrib/tasks/common/taskConfiguration.ts | 13 +-- .../workbench/contrib/tasks/common/tasks.ts | 13 +-- 7 files changed, 55 insertions(+), 119 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTask.ts b/src/vs/workbench/api/browser/mainThreadTask.ts index d2d28de0495..60cb8a79824 100644 --- a/src/vs/workbench/api/browser/mainThreadTask.ts +++ b/src/vs/workbench/api/browser/mainThreadTask.ts @@ -318,8 +318,6 @@ namespace TaskDTO { isBackground: task.configurationProperties.isBackground, problemMatchers: [], hasDefinedMatchers: ContributedTask.is(task) ? task.hasDefinedMatchers : false, - icon: task.configurationProperties.icon, - color: task.configurationProperties.color, runOptions: RunOptionsDTO.from(task.runOptions), }; result.group = TaskGroupDTO.from(task.configurationProperties.group); @@ -344,7 +342,7 @@ namespace TaskDTO { return result; } - export function to(task: ITaskDTO | undefined, workspace: IWorkspaceContextService, executeOnly: boolean): ContributedTask | undefined { + export function to(task: ITaskDTO | undefined, workspace: IWorkspaceContextService, executeOnly: boolean, icon?: { id: string; color?: string }): ContributedTask | undefined { if (!task || (typeof task.name !== 'string')) { return undefined; } @@ -385,8 +383,7 @@ namespace TaskDTO { isBackground: !!task.isBackground, problemMatchers: task.problemMatchers.slice(), detail: task.detail, - color: task.color, - icon: task.icon + icon } ); return result; @@ -495,7 +492,7 @@ export class MainThreadTask implements MainThreadTaskShape { dto.name = ((dto.name === undefined) ? '' : dto.name); // Using an empty name causes the name to default to the one given by the provider. return Promise.resolve(this._proxy.$resolveTask(handle, dto)).then(resolvedTask => { if (resolvedTask) { - return TaskDTO.to(resolvedTask, this._workspaceContextServer, true); + return TaskDTO.to(resolvedTask, this._workspaceContextServer, true, task.configurationProperties.icon); } return undefined; diff --git a/src/vs/workbench/api/common/shared/tasks.ts b/src/vs/workbench/api/common/shared/tasks.ts index 131552e725b..f7f206f0649 100644 --- a/src/vs/workbench/api/common/shared/tasks.ts +++ b/src/vs/workbench/api/common/shared/tasks.ts @@ -103,8 +103,6 @@ export interface ITaskDTO { problemMatchers: string[]; hasDefinedMatchers: boolean; runOptions?: IRunOptionsDTO; - color?: string; - icon?: string; } export interface ITaskSetDTO { diff --git a/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts b/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts index 0d307a071df..f7d122e7e15 100644 --- a/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts +++ b/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts @@ -9,17 +9,17 @@ import { Task, ContributedTask, CustomTask, ConfiguringTask, TaskSorter, KeyedTa import { IWorkspace, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import * as Types from 'vs/base/common/types'; import { ITaskService, IWorkspaceFolderTaskResult } from 'vs/workbench/contrib/tasks/common/taskService'; -import { IQuickPickItem, QuickPickInput, IQuickPick, IQuickInputButton, IQuickPickSeparator } from 'vs/base/parts/quickinput/common/quickInput'; +import { IQuickPickItem, QuickPickInput, IQuickPick, IQuickInputButton } from 'vs/base/parts/quickinput/common/quickInput'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { Codicon } from 'vs/base/common/codicons'; import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { getColorClass, getColorStyleElement, getStandardColors } from 'vs/workbench/contrib/terminal/browser/terminalIcon'; +import { getColorClass, getColorStyleElement } from 'vs/workbench/contrib/terminal/browser/terminalIcon'; import { TaskQuickPickEntryType } from 'vs/workbench/contrib/tasks/browser/abstractTaskService'; export const QUICKOPEN_DETAIL_CONFIG = 'task.quickOpen.detail'; @@ -78,25 +78,25 @@ export class TaskQuickPick extends Disposable { } public static getTaskLabelWithIcon(task: Task | ConfiguringTask): string { - return task.configurationProperties.icon ? `$(${task.configurationProperties.icon}) ${task._label}` : task.configurationProperties.color ? `$(${Codicon.tools.id}) ${task._label}` : `${task._label}`; + const icon = task.configurationProperties.icon; + if (!icon) { + return `${task._label}`; + } + return icon.id ? `$(${icon.id}) ${task._label}` : `$(${Codicon.tools.id}) ${task._label}`; } public static applyColorStyles(task: Task | ConfiguringTask, entry: TaskQuickPickEntryType | ITaskTwoLevelQuickPickEntry, themeService: IThemeService): void { - if (task.configurationProperties.color) { + if (task.configurationProperties.icon?.color) { const colorTheme = themeService.getColorTheme(); const styleElement = getColorStyleElement(colorTheme); - entry.iconClasses = [getColorClass(task.configurationProperties.color)]; + entry.iconClasses = [getColorClass(task.configurationProperties.icon.color)]; document.body.appendChild(styleElement); } } private _createTaskEntry(task: Task | ConfiguringTask, extraButtons: IQuickInputButton[] = []): ITaskTwoLevelQuickPickEntry { - const customizeIconButton = { iconClass: ThemeIcon.asClassName(Codicon.pencil), tooltip: nls.localize('setIconAndColor', "Choose color and icon") }; const entry: ITaskTwoLevelQuickPickEntry = { label: this._guessTaskLabel(task), description: this._taskService.getTaskDescription(task), task, detail: this._showDetail() ? task.configurationProperties.detail : undefined }; entry.buttons = []; - if (CustomTask.is(task)) { - entry.buttons.push(customizeIconButton); - } entry.buttons.push({ iconClass: ThemeIcon.asClassName(configureTaskIcon), tooltip: nls.localize('configureTask', "Configure Task") }); entry.buttons.push(...extraButtons); TaskQuickPick.applyColorStyles(task, entry, this._themeService); @@ -234,9 +234,6 @@ export class TaskQuickPick extends Disposable { if (indexToRemove >= 0) { picker.items = [...picker.items.slice(0, indexToRemove), ...picker.items.slice(indexToRemove + 1)]; } - } else if (context.button.iconClass = ThemeIcon.asClassName(Codicon.pencil)) { - await this._setColor(task); - await this._setIcon(task); } else { this._quickInputService.cancel(); if (ContributedTask.is(task)) { @@ -291,72 +288,6 @@ export class TaskQuickPick extends Disposable { return; } - private async _setColor(task: Task | ConfiguringTask | null | string | undefined): Promise { - if (task === undefined || task === null || typeof task === 'string') { - return; - } - const colorTheme = this._themeService.getColorTheme(); - const standardColors: string[] = getStandardColors(colorTheme); - const styleElement = getColorStyleElement(colorTheme); - const items: (IQuickPickItem | IQuickPickSeparator)[] = []; - for (const colorKey of standardColors) { - const colorClass = getColorClass(colorKey); - items.push({ - label: `$(${Codicon.circleFilled.id}) ${colorKey.replace('terminal.ansi', '')}`, id: colorKey, description: colorKey, iconClasses: [colorClass] - }); - } - items.push({ type: 'separator' }); - const showAllColorsItem = { label: 'Reset to default' }; - items.push(showAllColorsItem); - document.body.appendChild(styleElement); - - const quickPick = this._quickInputService.createQuickPick(); - quickPick.items = items; - quickPick.matchOnDescription = true; - quickPick.show(); - const disposables: IDisposable[] = []; - const result = await new Promise(r => { - disposables.push(quickPick.onDidHide(() => r(undefined))); - disposables.push(quickPick.onDidAccept(() => r(quickPick.selectedItems[0]))); - }); - dispose(disposables); - - if (result && task && typeof task !== 'string') { - task.configurationProperties.color = result.id; - } - document.body.removeChild(styleElement); - quickPick.hide(); - } - - private async _setIcon(task: Task | ConfiguringTask | null | string | undefined): Promise { - if (task === undefined || task === null || typeof task === 'string') { - return; - } - type Item = IQuickPickItem & { icon: ThemeIcon }; - const items: Item[] = []; - for (const icon of Codicon.getAll()) { - items.push({ label: `$(${icon.id})`, description: `${icon.id}`, id: icon.id, icon, iconClasses: task.configurationProperties.color ? [getColorClass(task.configurationProperties.color)] : undefined }); - } - - const quickPick = this._quickInputService.createQuickPick(); - quickPick.items = items; - quickPick.matchOnDescription = true; - quickPick.show(); - const disposables: IDisposable[] = []; - const result = await new Promise(r => { - disposables.push(quickPick.onDidHide(() => r(undefined))); - disposables.push(quickPick.onDidAccept(() => r(quickPick.selectedItems[0]))); - }); - dispose(disposables); - - if (result && task && typeof task !== 'string') { - task.configurationProperties.icon = result.id; - } - if (CustomTask.is(task) && result) { - await this._taskService.customize(task, { icon: result.id, color: task.configurationProperties.color }, false); - } - quickPick.hide(); - } private async _doPickerFirstLevel(picker: IQuickPick, taskQuickPickEntries: QuickPickInput[]): Promise { diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index a2ddb8bef5c..fa46cfb33ef 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -508,6 +508,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { for (const dependency of task.configurationProperties.dependsOn) { const dependencyTask = await resolver.resolve(dependency.uri, dependency.task!); if (dependencyTask) { + dependencyTask.configurationProperties.icon = task.configurationProperties.icon; const key = dependencyTask.getMapKey(); let promise = this._activeTasks[key] ? this._getDependencyPromise(this._activeTasks[key]) : undefined; if (!promise) { @@ -1033,7 +1034,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { }); let icon: URI | ThemeIcon | { light: URI; dark: URI } | undefined; if (task.configurationProperties.icon) { - icon = ThemeIcon.fromId(task.configurationProperties.icon); + icon = ThemeIcon.fromId(task.configurationProperties.icon.id); } else { const taskGroupKind = task.configurationProperties.group ? GroupKind.to(task.configurationProperties.group) : undefined; const kindId = typeof taskGroupKind === 'string' ? taskGroupKind : taskGroupKind?.kind; @@ -1046,7 +1047,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { args: defaultProfile.args, env: { ...defaultProfile.env }, icon, - color: task.configurationProperties.color || defaultProfile.color, + color: task.configurationProperties.icon?.color || undefined, waitOnExit }; let shellSpecified: boolean = false; @@ -1143,8 +1144,8 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { shellLaunchConfig = { name: terminalName, type, - icon: task.configurationProperties.icon ? ThemeIcon.fromId(task.configurationProperties.icon) : undefined, - color: task.configurationProperties.color, + icon: task.configurationProperties.icon?.id ? ThemeIcon.fromId(task.configurationProperties.icon.id) : undefined, + color: task.configurationProperties.icon?.color || undefined, executable: executable, args: args.map(a => Types.isString(a) ? a : a.value), waitOnExit @@ -1273,8 +1274,8 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { comment: ['The task command line or label'] }, 'Executing task: {0}', task._label), { excludeLeadingNewLine: true }) : undefined, isFeatureTerminal: true, - icon: task.configurationProperties.icon ? ThemeIcon.fromId(task.configurationProperties.icon) : undefined, - color: task.configurationProperties.color + icon: task.configurationProperties.icon?.id ? ThemeIcon.fromId(task.configurationProperties.icon.id) : undefined, + color: task.configurationProperties.icon?.color || undefined, }; } else { const resolvedResult: { command: CommandString; args: CommandString[] } = await this._resolveCommandAndArgs(resolver, task.command); diff --git a/src/vs/workbench/contrib/tasks/common/jsonSchema_v2.ts b/src/vs/workbench/contrib/tasks/common/jsonSchema_v2.ts index aa52f6e45a6..d455def1f6e 100644 --- a/src/vs/workbench/contrib/tasks/common/jsonSchema_v2.ts +++ b/src/vs/workbench/contrib/tasks/common/jsonSchema_v2.ts @@ -13,6 +13,7 @@ import { ProblemMatcherRegistry } from 'vs/workbench/contrib/tasks/common/proble import { TaskDefinitionRegistry } from './taskDefinitionRegistry'; import * as ConfigurationResolverUtils from 'vs/workbench/services/configurationResolver/common/configurationResolverUtils'; import { inputsSchema } from 'vs/workbench/services/configurationResolver/common/configurationResolverSchema'; +import { Codicon } from 'vs/base/common/codicons'; function fixReferences(literal: any) { if (Array.isArray(literal)) { @@ -94,14 +95,30 @@ const detail: IJSONSchema = { description: nls.localize('JsonSchema.tasks.detail', 'An optional description of a task that shows in the Run Task quick pick as a detail.') }; -const color: IJSONSchema = { - type: 'string', - description: nls.localize('JsonSchema.tasks.color', 'An optional color for the task icon') -}; - const icon: IJSONSchema = { - type: 'string', - description: nls.localize('JsonSchema.tasks.icon', 'An optional icon for the task') + type: 'object', + properties: { + id: { + description: nls.localize('JsonSchema.tasks.icon.id', 'An optional icon for the task'), + type: 'string', + enum: Array.from(Codicon.getAll(), icon => icon.id), + markdownEnumDescriptions: Array.from(Codicon.getAll(), icon => `$(${icon.id})`), + }, + color: { + description: nls.localize('JsonSchema.tasks.icon.color', 'An optional color to use for the task icon'), + type: ['string', 'null'], + enum: [ + 'terminal.ansiBlack', + 'terminal.ansiRed', + 'terminal.ansiGreen', + 'terminal.ansiYellow', + 'terminal.ansiBlue', + 'terminal.ansiMagenta', + 'terminal.ansiCyan', + 'terminal.ansiWhite' + ], + }, + } }; const presentation: IJSONSchema = { @@ -388,7 +405,6 @@ const taskConfiguration: IJSONSchema = { default: false }, presentation: Objects.deepClone(presentation), - color: Objects.deepClone(color), icon: Objects.deepClone(icon), options: options, problemMatcher: { @@ -467,8 +483,7 @@ taskDescriptionProperties.identifier = Objects.deepClone(identifier); taskDescriptionProperties.type = Objects.deepClone(taskType); taskDescriptionProperties.presentation = Objects.deepClone(presentation); taskDescriptionProperties.terminal = terminal; -taskDescriptionProperties.color = color; -taskDescriptionProperties.icon = icon; +taskDescriptionProperties.icon = Objects.deepClone(icon); taskDescriptionProperties.group = Objects.deepClone(group); taskDescriptionProperties.runOptions = Objects.deepClone(runOptions); taskDescriptionProperties.detail = detail; diff --git a/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts index 974a6371fb6..ed7b1403249 100644 --- a/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts +++ b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts @@ -356,7 +356,7 @@ export interface IConfigurationProperties { /** * The icon for this task in the terminal tabs list */ - icon?: string; + icon?: { id: string; color?: string }; /** * The icon's color in the terminal tabs list @@ -1322,7 +1322,6 @@ namespace ConfigurationProperties { { property: 'presentation', type: CommandConfiguration.PresentationOptions }, { property: 'problemMatchers' }, { property: 'options' }, - { property: 'color' }, { property: 'icon' } ]; @@ -1350,12 +1349,7 @@ namespace ConfigurationProperties { if (Types.isString(external.identifier)) { result.identifier = external.identifier; } - if (Types.isString(external.color)) { - result.color = external.color; - } - if (Types.isString(external.icon)) { - result.icon = external.icon; - } + result.icon = external.icon; if (external.isBackground !== undefined) { result.isBackground = !!external.isBackground; @@ -1641,8 +1635,7 @@ namespace CustomTask { { name: configuredProps.configurationProperties.name || contributedTask.configurationProperties.name, identifier: configuredProps.configurationProperties.identifier || contributedTask.configurationProperties.identifier, - icon: contributedTask.configurationProperties.icon, - color: contributedTask.configurationProperties.color + icon: contributedTask.configurationProperties.icon }, ); diff --git a/src/vs/workbench/contrib/tasks/common/tasks.ts b/src/vs/workbench/contrib/tasks/common/tasks.ts index 133e902e25f..1f366dfb6d9 100644 --- a/src/vs/workbench/contrib/tasks/common/tasks.ts +++ b/src/vs/workbench/contrib/tasks/common/tasks.ts @@ -548,12 +548,7 @@ export interface IConfigurationProperties { /** * The icon for this task in the terminal tabs list */ - icon?: string; - - /** - * The icon's color in the terminal tabs list - */ - color?: string; + icon?: { id: string; color?: string }; } export enum RunOnOptions { @@ -914,6 +909,11 @@ export class ContributedTask extends CommonTask { */ command: ICommandConfiguration; + /** + * The icon for the task + */ + icon: { id: string; color?: string } | undefined; + public constructor(id: string, source: IExtensionTaskSource, label: string, type: string | undefined, defines: KeyedTaskIdentifier, command: ICommandConfiguration, hasDefinedMatchers: boolean, runOptions: IRunOptions, configurationProperties: IConfigurationProperties) { @@ -921,6 +921,7 @@ export class ContributedTask extends CommonTask { this.defines = defines; this.hasDefinedMatchers = hasDefinedMatchers; this.command = command; + this.icon = configurationProperties.icon; } public override clone(): ContributedTask { From 87a99dba093285e9cc88151d1b47808c6e578ed7 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 15 Jun 2022 18:50:34 -0700 Subject: [PATCH 076/175] Speed up shell integration tests and improve reliability --- test/automation/src/settings.ts | 20 +++++++++++++++++++ test/automation/src/terminal.ts | 12 +++++++++-- .../src/areas/terminal/terminal-helpers.ts | 18 +++++++++++------ .../terminal-shellIntegration.test.ts | 19 +++++++++--------- 4 files changed, 52 insertions(+), 17 deletions(-) diff --git a/test/automation/src/settings.ts b/test/automation/src/settings.ts index cda80632998..49cd35a5daa 100644 --- a/test/automation/src/settings.ts +++ b/test/automation/src/settings.ts @@ -12,6 +12,12 @@ export class SettingsEditor { constructor(private code: Code, private editors: Editors, private editor: Editor, private quickaccess: QuickAccess) { } + /** + * Write a single setting key value pair. + * + * Warning: You may need to set `editor.wordWrap` to `"on"` if this is called with a really long + * setting. + */ async addUserSetting(setting: string, value: string): Promise { await this.openUserSettingsFile(); @@ -20,6 +26,20 @@ export class SettingsEditor { await this.editors.saveOpenedFile(); } + /** + * Write several settings faster than multiple calls to {@link addUserSetting}. + * + * Warning: You will likely also need to set `editor.wordWrap` to `"on"` if `addUserSetting` is + * called after this in the test. + */ + async addUserSettings(settings: [key: string, value: string][]): Promise { + await this.openUserSettingsFile(); + + await this.code.dispatchKeybinding('right'); + await this.editor.waitForTypeInEditor('settings.json', settings.map(v => `"${v[0]}": ${v[1]},`).join('')); + await this.editors.saveOpenedFile(); + } + async clearUserSettings(): Promise { await this.openUserSettingsFile(); await this.quickaccess.runCommand('editor.action.selectAll'); diff --git a/test/automation/src/terminal.ts b/test/automation/src/terminal.ts index 1dfeac42aeb..dfb24636716 100644 --- a/test/automation/src/terminal.ts +++ b/test/automation/src/terminal.ts @@ -83,8 +83,16 @@ export class Terminal { await this.code.dispatchKeybinding('enter'); await this.quickinput.waitForQuickInputClosed(); } - if (commandId === TerminalCommandId.Show || commandId === TerminalCommandId.CreateNewEditor || commandId === TerminalCommandId.CreateNew || commandId === TerminalCommandId.NewWithProfile) { - return await this._waitForTerminal(expectedLocation === 'editor' || commandId === TerminalCommandId.CreateNewEditor ? 'editor' : 'panel'); + switch (commandId) { + case TerminalCommandId.Show: + case TerminalCommandId.CreateNewEditor: + case TerminalCommandId.CreateNew: + case TerminalCommandId.NewWithProfile: + await this._waitForTerminal(expectedLocation === 'editor' || commandId === TerminalCommandId.CreateNewEditor ? 'editor' : 'panel'); + break; + case TerminalCommandId.KillAll: + await this.code.waitForElements(Selector.Xterm, true, e => e.length === 0); + break; } } diff --git a/test/smoke/src/areas/terminal/terminal-helpers.ts b/test/smoke/src/areas/terminal/terminal-helpers.ts index 24eccb0c2db..c333df0bc5a 100644 --- a/test/smoke/src/areas/terminal/terminal-helpers.ts +++ b/test/smoke/src/areas/terminal/terminal-helpers.ts @@ -5,12 +5,18 @@ import { Application } from '../../../../automation'; -export async function setTerminalTestSettings(app: Application) { - // Always show tabs to make getting terminal groups easier - await app.workbench.settingsEditor.addUserSetting('terminal.integrated.tabs.hideCondition', '"never"'); - // Use the DOM renderer for smoke tests so they can be inspected in the playwright trace - // viewer - await app.workbench.settingsEditor.addUserSetting('terminal.integrated.gpuAcceleration', '"off"'); +export async function setTerminalTestSettings(app: Application, additionalSettings: [key: string, value: string][] = []) { + await app.workbench.settingsEditor.addUserSettings([ + // Work wrap is required when calling settingsEditor.addUserSetting multiple times or the + // click to focus will fail + ['editor.wordWrap', '"on"'], + // Always show tabs to make getting terminal groups easier + ['terminal.integrated.tabs.hideCondition', '"never"'], + // Use the DOM renderer for smoke tests so they can be inspected in the playwright trace + // viewer + ['terminal.integrated.gpuAcceleration', '"off"'], + ...additionalSettings + ]); // Close the settings editor await app.workbench.quickaccess.runCommand('workbench.action.closeAllEditors'); diff --git a/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts b/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts index 24dc769beb8..75fa3ca8a65 100644 --- a/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts +++ b/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts @@ -17,18 +17,17 @@ export function setup() { app = this.app as Application; terminal = app.workbench.terminal; settingsEditor = app.workbench.settingsEditor; - await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.enabled', 'true'); - await setTerminalTestSettings(app); - }); - - after(async function () { - await settingsEditor.clearUserSettings(); + await setTerminalTestSettings(app, [['terminal.integrated.shellIntegration.enabled', 'true']]); }); afterEach(async function () { await app.workbench.terminal.runCommand(TerminalCommandId.KillAll); }); + after(async function () { + await settingsEditor.clearUserSettings(); + }); + async function createShellIntegrationProfile() { await terminal.runCommandWithValue(TerminalCommandIdWithValue.NewWithProfile, process.platform === 'win32' ? 'PowerShell' : 'bash'); } @@ -36,6 +35,7 @@ export function setup() { describe(`Shell integration ${i}`, function () { describe('Decorations', function () { describe('Should show default icons', function () { + it('Placeholder', async () => { await createShellIntegrationProfile(); await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 0 }); @@ -47,7 +47,7 @@ export function setup() { }); it('Error', async () => { await createShellIntegrationProfile(); - await terminal.runCommandInTerminal(`fsdkfsjdlfksjdkf`); + await terminal.runCommandInTerminal(`false`); await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 1 }); }); }); @@ -55,12 +55,13 @@ export function setup() { it('Should update and show custom icons', async () => { await createShellIntegrationProfile(); await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 0 }); - await terminal.runCommandInTerminal(`echo "success"`); - await terminal.runCommandInTerminal(`fsdkfsjdlfksjdkf`); + await terminal.runCommandInTerminal(`echo "foo"`); + await terminal.runCommandInTerminal(`bar`); await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationIcon', '"zap"'); await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationIconSuccess', '"zap"'); await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationIconError', '"zap"'); await terminal.assertCommandDecorations(undefined, { updatedIcon: "zap", count: 3 }); + await app.workbench.terminal.runCommand(TerminalCommandId.KillAll); }); }); }); From 1b0d0d00d49527d291c82441da323d8be4a9ebc5 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 15 Jun 2022 19:22:40 -0700 Subject: [PATCH 077/175] Enable terminal tests on desktop --- test/smoke/src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 42175985f48..3836601534f 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -400,7 +400,7 @@ describe(`VSCode Smoke Tests (${opts.web ? 'Web' : 'Electron'})`, () => { setupSearchTests(logger); setupNotebookTests(logger); setupLanguagesTests(logger); - if (opts.web) { setupTerminalTests(logger); } // Tests require playwright driver (https://github.com/microsoft/vscode/issues/146811) + setupTerminalTests(logger); setupStatusbarTests(logger); if (quality !== Quality.Dev && quality !== Quality.OSS) { setupExtensionTests(logger); } setupMultirootTests(logger); From 4fc5d76213092e8de7fc740de39657dad6ab906c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 15 Jun 2022 20:02:41 -0700 Subject: [PATCH 078/175] Fix ranges and validation setting for MD own path + header links (#152270) * Fix ranges and validation setting for MD own path + header links Previously for a `file.md`, links to headers in that file that use paths, such as `[link](./file.md#some-header)` were validated using `markdown.experimental.validate.fragmentLinks.enabled` This is confusing as that setting was only meant to be used for links such as`[link](#some-header`). It also resulted in the diagnostic having the incorrect range This change instead makes these links be validated by `markdown.experimental.validate.fileLinks.markdownFragmentLinks` * Fix compile --- .../src/languageFeatures/diagnostics.ts | 13 +++--- .../src/test/diagnostic.test.ts | 44 +++++++++++++++++++ 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts index 6a76b4614f3..2b8834adaa0 100644 --- a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts @@ -394,7 +394,7 @@ export class DiagnosticComputer { return { links, diagnostics: (await Promise.all([ - this.validateFileLinks(doc, options, links, token), + this.validateFileLinks(options, links, token), Array.from(this.validateReferenceLinks(options, links)), this.validateFragmentLinks(doc, options, links, token), ])).flat() @@ -415,6 +415,7 @@ export class DiagnosticComputer { const diagnostics: vscode.Diagnostic[] = []; for (const link of links) { if (link.href.kind === 'internal' + && link.source.text.startsWith('#') && link.href.path.toString() === doc.uri.toString() && link.href.fragment && !toc.lookup(link.href.fragment) @@ -449,14 +450,15 @@ export class DiagnosticComputer { } } - private async validateFileLinks(doc: SkinnyTextDocument, options: DiagnosticOptions, links: readonly MdLink[], token: vscode.CancellationToken): Promise { + private async validateFileLinks(options: DiagnosticOptions, links: readonly MdLink[], token: vscode.CancellationToken): Promise { const pathErrorSeverity = toSeverity(options.validateFileLinks); if (typeof pathErrorSeverity === 'undefined') { return []; } const fragmentErrorSeverity = toSeverity(typeof options.validateMarkdownFileLinkFragments === 'undefined' ? options.validateFragmentLinks : options.validateMarkdownFileLinkFragments); - const linkSet = new FileLinkMap(links); + // We've already validated our own fragment links in `validateOwnHeaderLinks` + const linkSet = new FileLinkMap(links.filter(link => !link.source.text.startsWith('#'))); if (linkSet.size === 0) { return []; } @@ -472,11 +474,6 @@ export class DiagnosticComputer { } const hrefDoc = await tryFindMdDocumentForLink({ kind: 'internal', path: path, fragment: '' }, this.workspaceContents); - if (hrefDoc && hrefDoc.uri.toString() === doc.uri.toString()) { - // We've already validated our own links in `validateOwnHeaderLinks` - return; - } - if (!hrefDoc && !await this.workspaceContents.pathExists(path)) { const msg = localize('invalidPathLink', 'File does not exist at path: {0}', path.fsPath); for (const link of links) { diff --git a/extensions/markdown-language-features/src/test/diagnostic.test.ts b/extensions/markdown-language-features/src/test/diagnostic.test.ts index cb6d32b244d..fe880e5bbd8 100644 --- a/extensions/markdown-language-features/src/test/diagnostic.test.ts +++ b/extensions/markdown-language-features/src/test/diagnostic.test.ts @@ -347,4 +347,48 @@ suite('markdown: Diagnostics', () => { new vscode.Range(5, 7, 5, 17), ]); }); + + test('Should generate diagnostics for non-existent header using file link to own file', async () => { + const doc = new InMemoryDocument(workspacePath('sub', 'doc.md'), joinLines( + `[bad](doc.md#no-such)`, + `[bad](doc#no-such)`, + `[bad](/sub/doc.md#no-such)`, + `[bad](/sub/doc#no-such)`, + )); + + const diagnostics = await getComputedDiagnostics(doc, new InMemoryWorkspaceMarkdownDocuments([doc])); + assertDiagnosticsEqual(orderDiagnosticsByRange(diagnostics), [ + new vscode.Range(0, 12, 0, 20), + new vscode.Range(1, 9, 1, 17), + new vscode.Range(2, 17, 2, 25), + new vscode.Range(3, 14, 3, 22), + ]); + }); + + test('Own header link using file path link should be controlled by "validateMarkdownFileLinkFragments" instead of "validateFragmentLinks"', async () => { + const doc = new InMemoryDocument(workspacePath('sub', 'doc.md'), joinLines( + `[bad](doc.md#no-such)`, + `[bad](doc#no-such)`, + `[bad](/sub/doc.md#no-such)`, + `[bad](/sub/doc#no-such)`, + )); + + const contents = new InMemoryWorkspaceMarkdownDocuments([doc]); + const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration({ + validateFragmentLinks: DiagnosticLevel.ignore, + validateMarkdownFileLinkFragments: DiagnosticLevel.warning, + })); + const { diagnostics } = await manager.recomputeDiagnosticState(doc, noopToken); + + assertDiagnosticsEqual(orderDiagnosticsByRange(diagnostics), [ + new vscode.Range(0, 12, 0, 20), + new vscode.Range(1, 9, 1, 17), + new vscode.Range(2, 17, 2, 25), + new vscode.Range(3, 14, 3, 22), + ]); + }); }); + +function orderDiagnosticsByRange(diagnostics: Iterable): readonly vscode.Diagnostic[] { + return Array.from(diagnostics).sort((a, b) => a.range.start.compareTo(b.range.start)); +} From b77fecfa101022c3fa0478a62dd429fe1f04224d Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 15 Jun 2022 20:25:34 -0700 Subject: [PATCH 079/175] Increase darwin timeout temporarily --- build/azure-pipelines/darwin/product-build-darwin-test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/azure-pipelines/darwin/product-build-darwin-test.yml b/build/azure-pipelines/darwin/product-build-darwin-test.yml index 6257382d7a3..a01e8951aaf 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-test.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-test.yml @@ -103,7 +103,7 @@ steps: set -e VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin-$(VSCODE_ARCH)" \ yarn smoketest-no-compile --web --tracing --headless - timeoutInMinutes: 20 + timeoutInMinutes: 30 displayName: Run smoke tests (Browser, Chromium) - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: @@ -112,7 +112,7 @@ steps: APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) APP_NAME="`ls $APP_ROOT | head -n 1`" yarn smoketest-no-compile --tracing --build "$APP_ROOT/$APP_NAME" - timeoutInMinutes: 20 + timeoutInMinutes: 30 displayName: Run smoke tests (Electron) - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: @@ -123,7 +123,7 @@ steps: APP_NAME="`ls $APP_ROOT | head -n 1`" VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH)" \ yarn smoketest-no-compile --tracing --remote --build "$APP_ROOT/$APP_NAME" - timeoutInMinutes: 20 + timeoutInMinutes: 30 displayName: Run smoke tests (Remote) - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: From d4e06aaa2b49ca03d9d18b4408a106168a050d5c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 15 Jun 2022 20:29:32 -0700 Subject: [PATCH 080/175] Disable windows shell integration tests only --- .../smoke/src/areas/terminal/terminal-shellIntegration.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts b/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts index 75fa3ca8a65..cfa5cefe7fd 100644 --- a/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts +++ b/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts @@ -32,7 +32,8 @@ export function setup() { await terminal.runCommandWithValue(TerminalCommandIdWithValue.NewWithProfile, process.platform === 'win32' ? 'PowerShell' : 'bash'); } - describe(`Shell integration ${i}`, function () { + // TODO: Some agents may not have pwsh installed? + (process.platform === 'win32' ? describe.skip : describe)(`Shell integration`, function () { describe('Decorations', function () { describe('Should show default icons', function () { From 8559324b0988ffac5cd6a1af35b77d279c927545 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 15 Jun 2022 20:33:58 -0700 Subject: [PATCH 081/175] Use more optional chaining in TS extension (#152271) Use optional chaining in TS extension Also removes `prefer-const` since this is now enabled globally --- extensions/typescript-language-features/.eslintrc.json | 2 +- .../src/languageFeatures/codeLens/baseCodeLensProvider.ts | 2 +- .../src/languageFeatures/signatureHelp.ts | 2 +- .../src/languageFeatures/tagClosing.ts | 2 +- .../src/tsServer/versionProvider.electron.ts | 4 ++-- .../src/typescriptServiceClient.ts | 2 +- extensions/typescript-language-features/src/utils/async.ts | 6 +++--- .../typescript-language-features/src/utils/codeAction.ts | 4 ++-- .../typescript-language-features/src/utils/configuration.ts | 2 +- .../typescript-language-features/src/utils/telemetry.ts | 2 +- 10 files changed, 14 insertions(+), 14 deletions(-) diff --git a/extensions/typescript-language-features/.eslintrc.json b/extensions/typescript-language-features/.eslintrc.json index ae8cc48c050..f7c6c1b495b 100644 --- a/extensions/typescript-language-features/.eslintrc.json +++ b/extensions/typescript-language-features/.eslintrc.json @@ -1,5 +1,5 @@ { "rules": { - "prefer-const": "error" + "@typescript-eslint/prefer-optional-chain": "warn" } } diff --git a/extensions/typescript-language-features/src/languageFeatures/codeLens/baseCodeLensProvider.ts b/extensions/typescript-language-features/src/languageFeatures/codeLens/baseCodeLensProvider.ts index 890486d7902..214e6b24f7b 100644 --- a/extensions/typescript-language-features/src/languageFeatures/codeLens/baseCodeLensProvider.ts +++ b/extensions/typescript-language-features/src/languageFeatures/codeLens/baseCodeLensProvider.ts @@ -88,7 +88,7 @@ export function getSymbolRange( } // In older versions, we have to calculate this manually. See #23924 - const span = item.spans && item.spans[0]; + const span = item.spans?.[0]; if (!span) { return undefined; } diff --git a/extensions/typescript-language-features/src/languageFeatures/signatureHelp.ts b/extensions/typescript-language-features/src/languageFeatures/signatureHelp.ts index 1d9c66caa09..8bde8b45df7 100644 --- a/extensions/typescript-language-features/src/languageFeatures/signatureHelp.ts +++ b/extensions/typescript-language-features/src/languageFeatures/signatureHelp.ts @@ -64,7 +64,7 @@ class TypeScriptSignatureHelpProvider implements vscode.SignatureHelpProvider { private getActiveParameter(info: Proto.SignatureHelpItems): number { const activeSignature = info.items[info.selectedItemIndex]; - if (activeSignature && activeSignature.isVariadic) { + if (activeSignature?.isVariadic) { return Math.min(info.argumentIndex, activeSignature.parameters.length - 1); } return info.argumentIndex; diff --git a/extensions/typescript-language-features/src/languageFeatures/tagClosing.ts b/extensions/typescript-language-features/src/languageFeatures/tagClosing.ts index aeaa1bbcaae..c2219b7b4ac 100644 --- a/extensions/typescript-language-features/src/languageFeatures/tagClosing.ts +++ b/extensions/typescript-language-features/src/languageFeatures/tagClosing.ts @@ -53,7 +53,7 @@ class TagClosing extends Disposable { return; } - const activeDocument = vscode.window.activeTextEditor && vscode.window.activeTextEditor.document; + const activeDocument = vscode.window.activeTextEditor?.document; if (document !== activeDocument) { return; } diff --git a/extensions/typescript-language-features/src/tsServer/versionProvider.electron.ts b/extensions/typescript-language-features/src/tsServer/versionProvider.electron.ts index 760f82d7b09..6355e5b60c4 100644 --- a/extensions/typescript-language-features/src/tsServer/versionProvider.electron.ts +++ b/extensions/typescript-language-features/src/tsServer/versionProvider.electron.ts @@ -28,7 +28,7 @@ export class DiskTypeScriptVersionProvider implements ITypeScriptVersionProvider public get globalVersion(): TypeScriptVersion | undefined { if (this.configuration?.globalTsdk) { const globals = this.loadVersionsFromSetting(TypeScriptVersionSource.UserSetting, this.configuration.globalTsdk); - if (globals && globals.length) { + if (globals?.length) { return globals[0]; } } @@ -37,7 +37,7 @@ export class DiskTypeScriptVersionProvider implements ITypeScriptVersionProvider public get localVersion(): TypeScriptVersion | undefined { const tsdkVersions = this.localTsdkVersions; - if (tsdkVersions && tsdkVersions.length) { + if (tsdkVersions?.length) { return tsdkVersions[0]; } diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index 0fdecc5c952..0585285b6d7 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -883,7 +883,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.loadingIndicator.reset(); const diagnosticEvent = event as Proto.DiagnosticEvent; - if (diagnosticEvent.body && diagnosticEvent.body.diagnostics) { + if (diagnosticEvent.body?.diagnostics) { this._onDiagnosticsReceived.fire({ kind: getDignosticsKind(event), resource: this.toResource(diagnosticEvent.body.file), diff --git a/extensions/typescript-language-features/src/utils/async.ts b/extensions/typescript-language-features/src/utils/async.ts index 75ccf258f28..db92754fd2e 100644 --- a/extensions/typescript-language-features/src/utils/async.ts +++ b/extensions/typescript-language-features/src/utils/async.ts @@ -13,7 +13,7 @@ export class Delayer { public defaultDelay: number; private timeout: any; // Timer - private completionPromise: Promise | null; + private completionPromise: Promise | null; private onSuccess: ((value: T | PromiseLike | undefined) => void) | null; private task: ITask | null; @@ -25,7 +25,7 @@ export class Delayer { this.task = null; } - public trigger(task: ITask, delay: number = this.defaultDelay): Promise { + public trigger(task: ITask, delay: number = this.defaultDelay): Promise { this.task = task; if (delay >= 0) { this.cancelTimeout(); @@ -37,7 +37,7 @@ export class Delayer { }).then(() => { this.completionPromise = null; this.onSuccess = null; - const result = this.task && this.task(); + const result = this.task?.(); this.task = null; return result; }); diff --git a/extensions/typescript-language-features/src/utils/codeAction.ts b/extensions/typescript-language-features/src/utils/codeAction.ts index df345e33517..6a7ad144482 100644 --- a/extensions/typescript-language-features/src/utils/codeAction.ts +++ b/extensions/typescript-language-features/src/utils/codeAction.ts @@ -12,7 +12,7 @@ export function getEditForCodeAction( client: ITypeScriptServiceClient, action: Proto.CodeAction ): vscode.WorkspaceEdit | undefined { - return action.changes && action.changes.length + return action.changes?.length ? typeConverters.WorkspaceEdit.fromFileCodeEdits(client, action.changes) : undefined; } @@ -36,7 +36,7 @@ export async function applyCodeActionCommands( commands: ReadonlyArray<{}> | undefined, token: vscode.CancellationToken, ): Promise { - if (commands && commands.length) { + if (commands?.length) { for (const command of commands) { await client.execute('applyCodeActionCommand', { command }, token); } diff --git a/extensions/typescript-language-features/src/utils/configuration.ts b/extensions/typescript-language-features/src/utils/configuration.ts index 2690f743848..efa442a5d0d 100644 --- a/extensions/typescript-language-features/src/utils/configuration.ts +++ b/extensions/typescript-language-features/src/utils/configuration.ts @@ -15,7 +15,7 @@ export enum TsServerLogLevel { export namespace TsServerLogLevel { export function fromString(value: string): TsServerLogLevel { - switch (value && value.toLowerCase()) { + switch (value?.toLowerCase()) { case 'normal': return TsServerLogLevel.Normal; case 'terse': diff --git a/extensions/typescript-language-features/src/utils/telemetry.ts b/extensions/typescript-language-features/src/utils/telemetry.ts index 2ef271de107..da7ee73cad0 100644 --- a/extensions/typescript-language-features/src/utils/telemetry.ts +++ b/extensions/typescript-language-features/src/utils/telemetry.ts @@ -55,7 +55,7 @@ export class VSCodeTelemetryReporter implements TelemetryReporter { @memoize private get reporter(): VsCodeTelemetryReporter | null { - if (this.packageInfo && this.packageInfo.aiKey) { + if (this.packageInfo?.aiKey) { this._reporter = new VsCodeTelemetryReporter( this.packageInfo.name, this.packageInfo.version, From 463a1e57657dc1eba790516b8fda5d621bd9a9a7 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 15 Jun 2022 20:59:19 -0700 Subject: [PATCH 082/175] Run shell integration tests a single time and revert timeout --- .../darwin/product-build-darwin-test.yml | 6 +- .../terminal-shellIntegration.test.ts | 102 +++++++++--------- 2 files changed, 53 insertions(+), 55 deletions(-) diff --git a/build/azure-pipelines/darwin/product-build-darwin-test.yml b/build/azure-pipelines/darwin/product-build-darwin-test.yml index a01e8951aaf..6257382d7a3 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-test.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-test.yml @@ -103,7 +103,7 @@ steps: set -e VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin-$(VSCODE_ARCH)" \ yarn smoketest-no-compile --web --tracing --headless - timeoutInMinutes: 30 + timeoutInMinutes: 20 displayName: Run smoke tests (Browser, Chromium) - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: @@ -112,7 +112,7 @@ steps: APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) APP_NAME="`ls $APP_ROOT | head -n 1`" yarn smoketest-no-compile --tracing --build "$APP_ROOT/$APP_NAME" - timeoutInMinutes: 30 + timeoutInMinutes: 20 displayName: Run smoke tests (Electron) - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: @@ -123,7 +123,7 @@ steps: APP_NAME="`ls $APP_ROOT | head -n 1`" VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH)" \ yarn smoketest-no-compile --tracing --remote --build "$APP_ROOT/$APP_NAME" - timeoutInMinutes: 30 + timeoutInMinutes: 20 displayName: Run smoke tests (Remote) - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: diff --git a/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts b/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts index cfa5cefe7fd..200ab28b019 100644 --- a/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts +++ b/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts @@ -7,66 +7,64 @@ import { Application, Terminal, SettingsEditor, TerminalCommandIdWithValue, Term import { setTerminalTestSettings } from './terminal-helpers'; export function setup() { - for (let i = 0; i < 100; i++) { - describe(`Terminal Shell Integration ${i}`, () => { - let terminal: Terminal; - let settingsEditor: SettingsEditor; - let app: Application; - // Acquire automation API - before(async function () { - app = this.app as Application; - terminal = app.workbench.terminal; - settingsEditor = app.workbench.settingsEditor; - await setTerminalTestSettings(app, [['terminal.integrated.shellIntegration.enabled', 'true']]); - }); + describe('Terminal Shell Integration', () => { + let terminal: Terminal; + let settingsEditor: SettingsEditor; + let app: Application; + // Acquire automation API + before(async function () { + app = this.app as Application; + terminal = app.workbench.terminal; + settingsEditor = app.workbench.settingsEditor; + await setTerminalTestSettings(app, [['terminal.integrated.shellIntegration.enabled', 'true']]); + }); - afterEach(async function () { - await app.workbench.terminal.runCommand(TerminalCommandId.KillAll); - }); + afterEach(async function () { + await app.workbench.terminal.runCommand(TerminalCommandId.KillAll); + }); - after(async function () { - await settingsEditor.clearUserSettings(); - }); + after(async function () { + await settingsEditor.clearUserSettings(); + }); - async function createShellIntegrationProfile() { - await terminal.runCommandWithValue(TerminalCommandIdWithValue.NewWithProfile, process.platform === 'win32' ? 'PowerShell' : 'bash'); - } + async function createShellIntegrationProfile() { + await terminal.runCommandWithValue(TerminalCommandIdWithValue.NewWithProfile, process.platform === 'win32' ? 'PowerShell' : 'bash'); + } - // TODO: Some agents may not have pwsh installed? - (process.platform === 'win32' ? describe.skip : describe)(`Shell integration`, function () { - describe('Decorations', function () { - describe('Should show default icons', function () { + // TODO: Some agents may not have pwsh installed? + (process.platform === 'win32' ? describe.skip : describe)(`Shell integration`, function () { + describe('Decorations', function () { + describe('Should show default icons', function () { - it('Placeholder', async () => { - await createShellIntegrationProfile(); - await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 0 }); - }); - it('Success', async () => { - await createShellIntegrationProfile(); - await terminal.runCommandInTerminal(`echo "success"`); - await terminal.assertCommandDecorations({ placeholder: 1, success: 1, error: 0 }); - }); - it('Error', async () => { - await createShellIntegrationProfile(); - await terminal.runCommandInTerminal(`false`); - await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 1 }); - }); + it('Placeholder', async () => { + await createShellIntegrationProfile(); + await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 0 }); }); - describe('Custom configuration', function () { - it('Should update and show custom icons', async () => { - await createShellIntegrationProfile(); - await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 0 }); - await terminal.runCommandInTerminal(`echo "foo"`); - await terminal.runCommandInTerminal(`bar`); - await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationIcon', '"zap"'); - await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationIconSuccess', '"zap"'); - await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationIconError', '"zap"'); - await terminal.assertCommandDecorations(undefined, { updatedIcon: "zap", count: 3 }); - await app.workbench.terminal.runCommand(TerminalCommandId.KillAll); - }); + it('Success', async () => { + await createShellIntegrationProfile(); + await terminal.runCommandInTerminal(`echo "success"`); + await terminal.assertCommandDecorations({ placeholder: 1, success: 1, error: 0 }); + }); + it('Error', async () => { + await createShellIntegrationProfile(); + await terminal.runCommandInTerminal(`false`); + await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 1 }); + }); + }); + describe('Custom configuration', function () { + it('Should update and show custom icons', async () => { + await createShellIntegrationProfile(); + await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 0 }); + await terminal.runCommandInTerminal(`echo "foo"`); + await terminal.runCommandInTerminal(`bar`); + await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationIcon', '"zap"'); + await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationIconSuccess', '"zap"'); + await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationIconError', '"zap"'); + await terminal.assertCommandDecorations(undefined, { updatedIcon: "zap", count: 3 }); + await app.workbench.terminal.runCommand(TerminalCommandId.KillAll); }); }); }); }); - } + }); } From 6f7c824a826ff0ccaf4de05d6fe0aac3be7bc136 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 16 Jun 2022 06:37:22 +0200 Subject: [PATCH 083/175] adopt to application scope (#152286) --- .../common/extensionStorage.ts | 7 ++-- .../userDataSync/common/globalStateSync.ts | 2 + .../experiments/common/experimentService.ts | 40 +++++++++---------- .../experimentService.test.ts | 2 +- .../browser/extensionsWorkbenchService.ts | 2 +- .../userDataSync/browser/userDataSync.ts | 4 +- 6 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionStorage.ts b/src/vs/platform/extensionManagement/common/extensionStorage.ts index 13ddc8bd4f5..3e9a3059f98 100644 --- a/src/vs/platform/extensionManagement/common/extensionStorage.ts +++ b/src/vs/platform/extensionManagement/common/extensionStorage.ts @@ -55,6 +55,7 @@ export class ExtensionStorageService extends Disposable implements IExtensionSto return undefined; } + /* TODO @sandy081: This has to be done across all profiles */ static async removeOutdatedExtensionVersions(extensionManagementService: IExtensionManagementService, storageService: IStorageService): Promise { const extensions = await extensionManagementService.getInstalled(); const extensionVersionsToRemove: string[] = []; @@ -193,7 +194,7 @@ export class ExtensionStorageService extends Disposable implements IExtensionSto } private get migrationList(): [string, string][] { - const value = this.storageService.get('extensionStorage.migrationList', StorageScope.GLOBAL, '[]'); + const value = this.storageService.get('extensionStorage.migrationList', StorageScope.APPLICATION, '[]'); try { const migrationList = JSON.parse(value); if (isArray(migrationList)) { @@ -205,9 +206,9 @@ export class ExtensionStorageService extends Disposable implements IExtensionSto private set migrationList(migrationList: [string, string][]) { if (migrationList.length) { - this.storageService.store('extensionStorage.migrationList', JSON.stringify(migrationList), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store('extensionStorage.migrationList', JSON.stringify(migrationList), StorageScope.APPLICATION, StorageTarget.MACHINE); } else { - this.storageService.remove('extensionStorage.migrationList', StorageScope.GLOBAL); + this.storageService.remove('extensionStorage.migrationList', StorageScope.APPLICATION); } } diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts index 1592622196b..a1b6c768ed4 100644 --- a/src/vs/platform/userDataSync/common/globalStateSync.ts +++ b/src/vs/platform/userDataSync/common/globalStateSync.ts @@ -56,6 +56,8 @@ function stringify(globalState: IGlobalState, format: boolean): string { const GLOBAL_STATE_DATA_VERSION = 1; /** + * TODO: @sandy081: Sync only global state of default profile + * * Synchronises global state that includes * - Global storage with user scope * - Locale from argv properties diff --git a/src/vs/workbench/contrib/experiments/common/experimentService.ts b/src/vs/workbench/contrib/experiments/common/experimentService.ts index c66a550ef63..235217dbc64 100644 --- a/src/vs/workbench/contrib/experiments/common/experimentService.ts +++ b/src/vs/workbench/contrib/experiments/common/experimentService.ts @@ -223,9 +223,9 @@ export class ExperimentService extends Disposable implements IExperimentService public markAsCompleted(experimentId: string): void { const storageKey = 'experiments.' + experimentId; - const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); + const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.APPLICATION), {}); experimentState.state = ExperimentState.Complete; - this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.APPLICATION, StorageTarget.MACHINE); } protected async getExperiments(): Promise { @@ -255,11 +255,11 @@ export class ExperimentService extends Disposable implements IExperimentService return this.getExperiments().then(rawExperiments => { // Offline mode if (!rawExperiments) { - const allExperimentIdsFromStorage = safeParse(this.storageService.get('allExperiments', StorageScope.GLOBAL), []); + const allExperimentIdsFromStorage = safeParse(this.storageService.get('allExperiments', StorageScope.APPLICATION), []); if (Array.isArray(allExperimentIdsFromStorage)) { allExperimentIdsFromStorage.forEach(experimentId => { const storageKey = 'experiments.' + experimentId; - const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), null); + const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.APPLICATION), null); if (experimentState) { this._experiments.push({ id: experimentId, @@ -278,19 +278,19 @@ export class ExperimentService extends Disposable implements IExperimentService rawExperiments = rawExperiments.filter(e => (e.schemaVersion || 0) <= currentSchemaVersion); // Clear disbaled/deleted experiments from storage - const allExperimentIdsFromStorage = safeParse(this.storageService.get('allExperiments', StorageScope.GLOBAL), []); + const allExperimentIdsFromStorage = safeParse(this.storageService.get('allExperiments', StorageScope.APPLICATION), []); const enabledExperiments = rawExperiments.filter(experiment => !!experiment.enabled).map(experiment => experiment.id.toLowerCase()); if (Array.isArray(allExperimentIdsFromStorage)) { allExperimentIdsFromStorage.forEach(experiment => { if (enabledExperiments.indexOf(experiment) === -1) { - this.storageService.remove(`experiments.${experiment}`, StorageScope.GLOBAL); + this.storageService.remove(`experiments.${experiment}`, StorageScope.APPLICATION); } }); } if (enabledExperiments.length) { - this.storageService.store('allExperiments', JSON.stringify(enabledExperiments), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store('allExperiments', JSON.stringify(enabledExperiments), StorageScope.APPLICATION, StorageTarget.MACHINE); } else { - this.storageService.remove('allExperiments', StorageScope.GLOBAL); + this.storageService.remove('allExperiments', StorageScope.APPLICATION); } const activationEvents = new Set(rawExperiments.map(exp => exp.condition?.activationEvent?.event) @@ -348,7 +348,7 @@ export class ExperimentService extends Disposable implements IExperimentService } const storageKey = 'experiments.' + experiment.id; - const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); + const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.APPLICATION), {}); if (!experimentState.hasOwnProperty('enabled')) { experimentState.enabled = processedExperiment.enabled; } @@ -360,7 +360,7 @@ export class ExperimentService extends Disposable implements IExperimentService return this.shouldRunExperiment(experiment, processedExperiment).then((state: ExperimentState) => { experimentState.state = processedExperiment.state = state; - this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.APPLICATION, StorageTarget.MACHINE); if (state === ExperimentState.Run) { this.fireRunExperiment(processedExperiment); @@ -372,7 +372,7 @@ export class ExperimentService extends Disposable implements IExperimentService private fireRunExperiment(experiment: IExperiment) { this._onExperimentEnabled.fire(experiment); - const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.GLOBAL), []); + const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.APPLICATION), []); if (runExperimentIdsFromStorage.indexOf(experiment.id) === -1) { runExperimentIdsFromStorage.push(experiment.id); } @@ -380,14 +380,14 @@ export class ExperimentService extends Disposable implements IExperimentService // Ensure we dont store duplicates const distinctExperiments = distinct(runExperimentIdsFromStorage); if (runExperimentIdsFromStorage.length !== distinctExperiments.length) { - this.storageService.store('currentOrPreviouslyRunExperiments', JSON.stringify(distinctExperiments), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store('currentOrPreviouslyRunExperiments', JSON.stringify(distinctExperiments), StorageScope.APPLICATION, StorageTarget.MACHINE); } } private checkExperimentDependencies(experiment: IRawExperiment): boolean { const experimentsPreviouslyRun = experiment.condition?.experimentsPreviouslyRun; if (experimentsPreviouslyRun) { - const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.GLOBAL), []); + const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.APPLICATION), []); let includeCheck = true; let excludeCheck = true; const includes = experimentsPreviouslyRun.includes; @@ -407,9 +407,9 @@ export class ExperimentService extends Disposable implements IExperimentService private recordActivatedEvent(event: string) { const key = experimentEventStorageKey(event); - const record = getCurrentActivationRecord(safeParse(this.storageService.get(key, StorageScope.GLOBAL), undefined)); + const record = getCurrentActivationRecord(safeParse(this.storageService.get(key, StorageScope.APPLICATION), undefined)); record.count[0]++; - this.storageService.store(key, JSON.stringify(record), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(key, JSON.stringify(record), StorageScope.APPLICATION, StorageTarget.MACHINE); this._experiments .filter(e => { @@ -434,7 +434,7 @@ export class ExperimentService extends Disposable implements IExperimentService const events = typeof setting.event === 'string' ? [setting.event] : setting.event; for (const event of events) { - const { count } = getCurrentActivationRecord(safeParse(this.storageService.get(experimentEventStorageKey(event), StorageScope.GLOBAL), undefined)); + const { count } = getCurrentActivationRecord(safeParse(this.storageService.get(experimentEventStorageKey(event), StorageScope.APPLICATION), undefined)); for (const entry of count) { if (entry > 0) { @@ -532,7 +532,7 @@ export class ExperimentService extends Disposable implements IExperimentService } const storageKey = 'experiments.' + experiment.id; - const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); + const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.APPLICATION), {}); return extensionsCheckPromise.then(success => { const fileEdits = condition.fileEdits; @@ -549,7 +549,7 @@ export class ExperimentService extends Disposable implements IExperimentService // Process model-save event every 250ms to reduce load const onModelsSavedWorker = this._register(new RunOnceWorker(models => { const date = new Date().toDateString(); - const latestExperimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); + const latestExperimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.APPLICATION), {}); if (latestExperimentState.state !== ExperimentState.Evaluating) { onSaveHandler.dispose(); onModelsSavedWorker.dispose(); @@ -579,12 +579,12 @@ export class ExperimentService extends Disposable implements IExperimentService if (filePathCheck && workspaceCheck) { latestExperimentState.editCount = (latestExperimentState.editCount || 0) + 1; latestExperimentState.lastEditedDate = date; - this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.APPLICATION, StorageTarget.MACHINE); } }); if (typeof latestExperimentState.editCount === 'number' && latestExperimentState.editCount >= fileEdits.minEditCount) { processedExperiment.state = latestExperimentState.state = (typeof condition.userProbability === 'number' && Math.random() < condition.userProbability && this.checkExperimentDependencies(experiment)) ? ExperimentState.Run : ExperimentState.NoRun; - this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.APPLICATION, StorageTarget.MACHINE); if (latestExperimentState.state === ExperimentState.Run && processedExperiment.action && ExperimentActionType[processedExperiment.action.type] === ExperimentActionType.Prompt) { this.fireRunExperiment(processedExperiment); } diff --git a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts index a97d0ace12a..2dbfdafd694 100644 --- a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts +++ b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts @@ -535,7 +535,7 @@ suite('Experiment Service', () => { didGetCall = true; assert.strictEqual(key, 'experimentEventRecord-my-event'); assert.deepStrictEqual(JSON.parse(value).count, [1, 0, 10, 0, 0, 0, 0]); - assert.strictEqual(scope, StorageScope.GLOBAL); + assert.strictEqual(scope, StorageScope.APPLICATION); } }); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 355bfcfeb41..2c7618d29fc 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -1568,7 +1568,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension }).then(undefined, error => this.onError(error)); } - + /* TODO: @sandy081 Extension version shall be moved to extensions.json file */ private _ignoredAutoUpdateExtensions: string[] | undefined; private get ignoredAutoUpdateExtensions(): string[] { if (!this._ignoredAutoUpdateExtensions) { diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 736ead6c5bb..05d341c2206 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -190,7 +190,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo if (requiresInitialization && !this.userDataSyncEnablementService.isEnabled()) { this.updateSyncAfterInitializationContext(true); } else { - this.updateSyncAfterInitializationContext(this.storageService.getBoolean(CONTEXT_SYNC_AFTER_INITIALIZATION.key, StorageScope.GLOBAL, false)); + this.updateSyncAfterInitializationContext(this.storageService.getBoolean(CONTEXT_SYNC_AFTER_INITIALIZATION.key, StorageScope.APPLICATION, false)); } const disposable = this._register(this.userDataSyncEnablementService.onDidChangeEnablement(() => { if (this.userDataSyncEnablementService.isEnabled()) { @@ -201,7 +201,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } private async updateSyncAfterInitializationContext(value: boolean): Promise { - this.storageService.store(CONTEXT_SYNC_AFTER_INITIALIZATION.key, value, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(CONTEXT_SYNC_AFTER_INITIALIZATION.key, value, StorageScope.APPLICATION, StorageTarget.MACHINE); this.syncAfterInitializationContext.set(value); this.updateGlobalActivityBadge(); } From 192f8bf12b9afb7b8e78f2dc54f2979bd3455494 Mon Sep 17 00:00:00 2001 From: John Murray Date: Thu, 16 Jun 2022 13:28:36 +0100 Subject: [PATCH 084/175] `Keep Editors Open` -> `Disable Preview Mode` (fixes #152240) (#152322) * `Keep Editors Open` -> `Disable Preview Mode` (fixes #152240) * Change localize key to ensure new translation --- src/vs/workbench/browser/parts/editor/editor.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 6343fd24e4f..2385271301b 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -349,7 +349,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_DIFF_SID MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: SHOW_EDITORS_IN_GROUP, title: localize('showOpenedEditors', "Show Opened Editors") }, group: '3_open', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: localize('closeAll', "Close All") }, group: '5_close', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: CLOSE_SAVED_EDITORS_COMMAND_ID, title: localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20 }); -MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_KEEP_EDITORS_COMMAND_ID, title: localize('toggleKeepEditors', "Keep Editors Open"), toggled: ContextKeyExpr.not('config.workbench.editor.enablePreview') }, group: '7_settings', order: 10 }); +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_KEEP_EDITORS_COMMAND_ID, title: localize('togglePreviewMode', "Disable Preview Mode"), toggled: ContextKeyExpr.not('config.workbench.editor.enablePreview') }, group: '7_settings', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_LOCK_GROUP_COMMAND_ID, title: localize('lockGroup', "Lock Group"), toggled: ActiveEditorGroupLockedContext }, group: '8_lock', order: 10, when: MultipleEditorGroupsContext }); interface IEditorToolItem { id: string; title: string; icon?: { dark?: URI; light?: URI } | ThemeIcon } From 3530e85ba80b3fc080bd7ad7c25e3481068eb3cb Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Thu, 16 Jun 2022 15:02:37 +0200 Subject: [PATCH 085/175] Properly return a non 0 exit code in case a test times out and fix tests which used to time out (#152329) --- .../common/userDataAutoSyncService.test.ts | 32 +++++++++---------- test/unit/browser/index.js | 17 +++++++--- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts b/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts index 242bf993092..cc94faefa1a 100644 --- a/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts +++ b/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts @@ -33,7 +33,7 @@ suite('UserDataAutoSyncService', () => { teardown(() => disposableStore.clear()); test('test auto sync with sync resource change triggers sync', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { // Setup the client const target = new UserDataSyncTestServer(); const client = disposableStore.add(new UserDataSyncClient(target)); @@ -57,7 +57,7 @@ suite('UserDataAutoSyncService', () => { }); test('test auto sync with sync resource change triggers sync for every change', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { // Setup the client const target = new UserDataSyncTestServer(); const client = disposableStore.add(new UserDataSyncClient(target)); @@ -85,7 +85,7 @@ suite('UserDataAutoSyncService', () => { }); test('test auto sync with non sync resource change triggers sync', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { // Setup the client const target = new UserDataSyncTestServer(); const client = disposableStore.add(new UserDataSyncClient(target)); @@ -109,7 +109,7 @@ suite('UserDataAutoSyncService', () => { }); test('test auto sync with non sync resource change does not trigger continuous syncs', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { // Setup the client const target = new UserDataSyncTestServer(); const client = disposableStore.add(new UserDataSyncClient(target)); @@ -135,7 +135,7 @@ suite('UserDataAutoSyncService', () => { }); test('test first auto sync requests', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { // Setup the client const target = new UserDataSyncTestServer(); const client = disposableStore.add(new UserDataSyncClient(target)); @@ -175,7 +175,7 @@ suite('UserDataAutoSyncService', () => { }); test('test further auto sync requests without changes', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { // Setup the client const target = new UserDataSyncTestServer(); const client = disposableStore.add(new UserDataSyncClient(target)); @@ -196,7 +196,7 @@ suite('UserDataAutoSyncService', () => { }); test('test further auto sync requests with changes', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { // Setup the client const target = new UserDataSyncTestServer(); const client = disposableStore.add(new UserDataSyncClient(target)); @@ -233,7 +233,7 @@ suite('UserDataAutoSyncService', () => { }); test('test auto sync send execution id header', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { // Setup the client const target = new UserDataSyncTestServer(); const client = disposableStore.add(new UserDataSyncClient(target)); @@ -258,7 +258,7 @@ suite('UserDataAutoSyncService', () => { }); test('test delete on one client throws turned off error on other client while syncing', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { const target = new UserDataSyncTestServer(); // Set up and sync from the client @@ -294,7 +294,7 @@ suite('UserDataAutoSyncService', () => { }); test('test disabling the machine turns off sync', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { const target = new UserDataSyncTestServer(); // Set up and sync from the test client @@ -328,7 +328,7 @@ suite('UserDataAutoSyncService', () => { }); test('test removing the machine adds machine back', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { const target = new UserDataSyncTestServer(); // Set up and sync from the test client @@ -353,7 +353,7 @@ suite('UserDataAutoSyncService', () => { }); test('test creating new session from one client throws session expired error on another client while syncing', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { const target = new UserDataSyncTestServer(); // Set up and sync from the client @@ -392,7 +392,7 @@ suite('UserDataAutoSyncService', () => { }); test('test rate limit on server', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { const target = new UserDataSyncTestServer(5); // Set up and sync from the test client @@ -412,7 +412,7 @@ suite('UserDataAutoSyncService', () => { }); test('test auto sync is suspended when server donot accepts requests', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { const target = new UserDataSyncTestServer(5, 1); // Set up and sync from the test client @@ -432,7 +432,7 @@ suite('UserDataAutoSyncService', () => { }); test('test cache control header with no cache is sent when triggered with disable cache option', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { const target = new UserDataSyncTestServer(5, 1); // Set up and sync from the test client @@ -446,7 +446,7 @@ suite('UserDataAutoSyncService', () => { }); test('test cache control header is not sent when triggered without disable cache option', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { const target = new UserDataSyncTestServer(5, 1); // Set up and sync from the test client diff --git a/test/unit/browser/index.js b/test/unit/browser/index.js index cd0682bfb99..305d78b819a 100644 --- a/test/unit/browser/index.js +++ b/test/unit/browser/index.js @@ -147,18 +147,22 @@ async function runTestsInBrowser(testModules, browserType) { withReporter(browserType, new EchoRunner(emitter, browserType.toUpperCase())); // collection failures for console printing - const fails = []; + const failingModuleIds = []; + const failingTests = []; emitter.on('fail', (test, err) => { if (err.stack) { const regex = /(vs\/.*\.test)\.js/; for (const line of String(err.stack).split('\n')) { const match = regex.exec(line); if (match) { - fails.push(match[1]); - break; + failingModuleIds.push(match[1]); + return; } } } + + // We could not determine the module id + failingTests.push(test.fullTitle); }); try { @@ -172,8 +176,11 @@ async function runTestsInBrowser(testModules, browserType) { } await browser.close(); - if (fails.length > 0) { - return `to DEBUG, open ${browserType.toUpperCase()} and navigate to ${target.href}?${fails.map(module => `m=${module}`).join('&')}`; + if (failingModuleIds.length > 0) { + return `to DEBUG, open ${browserType.toUpperCase()} and navigate to ${target.href}?${failingModuleIds.map(module => `m=${module}`).join('&')}`; + } + if (failingTests.length > 0) { + return `The followings tests are failing:\n - ${failingTests.join('\n - ')}`; } } From a3fe558d29b3c440edb14ae833d4f702ffb85366 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 16 Jun 2022 06:29:29 -0700 Subject: [PATCH 086/175] Improve screenreader alert when pausing (#152293) Fixes #151664 --- src/vs/workbench/contrib/debug/browser/debugService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 398674d57aa..b308716a8bb 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -870,8 +870,8 @@ export class DebugService implements IDebugService { const lineNumber = stackFrame.range.startLineNumber; if (lineNumber >= 1 && lineNumber <= model.getLineCount()) { const lineContent = control.getModel().getLineContent(lineNumber); - aria.alert(nls.localize({ key: 'debuggingPaused', comment: ['First placeholder is the stack frame name, second is the line number, third placeholder is the reason why debugging is stopped, for example "breakpoint" and the last one is the file line content.'] }, - "{0}:{1}, debugging paused {2}, {3}", stackFrame.source ? stackFrame.source.name : '', stackFrame.range.startLineNumber, thread && thread.stoppedDetails ? `, reason ${thread.stoppedDetails.reason}` : '', lineContent)); + aria.alert(nls.localize({ key: 'debuggingPaused', comment: ['First placeholder is the file line content, second placeholder is the reason why debugging is stopped, for example "breakpoint", third is the stack frame name, and last is the line number.'] }, + "{0}, debugging paused {1}, {2}:{3}", lineContent, thread && thread.stoppedDetails ? `, reason ${thread.stoppedDetails.reason}` : '', stackFrame.source ? stackFrame.source.name : '', stackFrame.range.startLineNumber)); } } } From 395380a6b7560f9d67323b98ebdd392805f819b7 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 16 Jun 2022 06:31:13 -0700 Subject: [PATCH 087/175] Remove `getOrDefault` (#152258) We can safely use `??` for this instead --- src/vs/base/browser/ui/list/listView.ts | 21 +++++++++---------- src/vs/base/common/objects.ts | 5 ----- .../common/extensionGalleryService.ts | 3 +-- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index c776ecec38b..a396809facb 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -15,7 +15,6 @@ import { Delayer, disposableTimeout } from 'vs/base/common/async'; import { memoize } from 'vs/base/common/decorators'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, DisposableStore, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { getOrDefault } from 'vs/base/common/objects'; import { IRange, Range } from 'vs/base/common/range'; import { INewScrollDimensions, Scrollable, ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable'; import { ISpliceable } from 'vs/base/common/sequence'; @@ -325,7 +324,7 @@ export class ListView implements ISpliceable, IDisposable { this.domNode.classList.toggle('mouse-support', typeof options.mouseSupport === 'boolean' ? options.mouseSupport : true); - this._horizontalScrolling = getOrDefault(options, o => o.horizontalScrolling, DefaultOptions.horizontalScrolling); + this._horizontalScrolling = options.horizontalScrolling ?? DefaultOptions.horizontalScrolling; this.domNode.classList.toggle('horizontal-scrolling', this._horizontalScrolling); this.additionalScrollHeight = typeof options.additionalScrollHeight === 'undefined' ? 0 : options.additionalScrollHeight; @@ -335,7 +334,7 @@ export class ListView implements ISpliceable, IDisposable { this.rowsContainer = document.createElement('div'); this.rowsContainer.className = 'monaco-list-rows'; - const transformOptimization = getOrDefault(options, o => o.transformOptimization, DefaultOptions.transformOptimization); + const transformOptimization = options.transformOptimization ?? DefaultOptions.transformOptimization; if (transformOptimization) { this.rowsContainer.style.transform = 'translate3d(0px, 0px, 0px)'; } @@ -344,14 +343,14 @@ export class ListView implements ISpliceable, IDisposable { this.scrollable = new Scrollable({ forceIntegerValues: true, - smoothScrollDuration: getOrDefault(options, o => o.smoothScrolling, false) ? 125 : 0, + smoothScrollDuration: (options.smoothScrolling ?? false) ? 125 : 0, scheduleAtNextAnimationFrame: cb => scheduleAtNextAnimationFrame(cb) }); this.scrollableElement = this.disposables.add(new SmoothScrollableElement(this.rowsContainer, { - alwaysConsumeMouseWheel: getOrDefault(options, o => o.alwaysConsumeMouseWheel, DefaultOptions.alwaysConsumeMouseWheel), + alwaysConsumeMouseWheel: options.alwaysConsumeMouseWheel ?? DefaultOptions.alwaysConsumeMouseWheel, horizontal: ScrollbarVisibility.Auto, - vertical: getOrDefault(options, o => o.verticalScrollMode, DefaultOptions.verticalScrollMode), - useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows), + vertical: options.verticalScrollMode ?? DefaultOptions.verticalScrollMode, + useShadows: options.useShadows ?? DefaultOptions.useShadows, mouseWheelScrollSensitivity: options.mouseWheelScrollSensitivity, fastScrollSensitivity: options.fastScrollSensitivity }, this.scrollable)); @@ -371,10 +370,10 @@ export class ListView implements ISpliceable, IDisposable { this.disposables.add(addDisposableListener(this.domNode, 'dragleave', e => this.onDragLeave(this.toDragEvent(e)))); this.disposables.add(addDisposableListener(this.domNode, 'dragend', e => this.onDragEnd(e))); - this.setRowLineHeight = getOrDefault(options, o => o.setRowLineHeight, DefaultOptions.setRowLineHeight); - this.setRowHeight = getOrDefault(options, o => o.setRowHeight, DefaultOptions.setRowHeight); - this.supportDynamicHeights = getOrDefault(options, o => o.supportDynamicHeights, DefaultOptions.supportDynamicHeights); - this.dnd = getOrDefault, IListViewDragAndDrop>(options, o => o.dnd, DefaultOptions.dnd); + this.setRowLineHeight = options.setRowLineHeight ?? DefaultOptions.setRowLineHeight; + this.setRowHeight = options.setRowHeight ?? DefaultOptions.setRowHeight; + this.supportDynamicHeights = options.supportDynamicHeights ?? DefaultOptions.supportDynamicHeights; + this.dnd = options.dnd ?? DefaultOptions.dnd; this.layout(); } diff --git a/src/vs/base/common/objects.ts b/src/vs/base/common/objects.ts index 62e4aeac226..7c7c4483bd9 100644 --- a/src/vs/base/common/objects.ts +++ b/src/vs/base/common/objects.ts @@ -186,11 +186,6 @@ export function safeStringify(obj: any): string { }); } -export function getOrDefault(obj: T, fn: (obj: T) => R | undefined, defaultValue: R): R { - const result = fn(obj); - return typeof result === 'undefined' ? defaultValue : result; -} - type obj = { [key: string]: any }; /** * Returns an object that has keys for each value that is different in the base object. Keys diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index b392af1f2b9..6f04a76edb6 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -7,7 +7,6 @@ import { distinct } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IStringDictionary } from 'vs/base/common/collections'; import { CancellationError, getErrorMessage, isCancellationError } from 'vs/base/common/errors'; -import { getOrDefault } from 'vs/base/common/objects'; import { IPager } from 'vs/base/common/paging'; import { isWeb, platform } from 'vs/base/common/platform'; import { arch } from 'vs/base/common/process'; @@ -709,7 +708,7 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi } let text = options.text || ''; - const pageSize = getOrDefault(options, o => o.pageSize, 50); + const pageSize = options.pageSize ?? 50; let query = new Query() .withPage(1, pageSize); From 46d9d7acda72fc721ea3d021f88393cabc9e95f5 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Thu, 16 Jun 2022 16:25:03 +0200 Subject: [PATCH 088/175] Git - branch protection (#152218) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: João Moreno --- extensions/git/src/commands.ts | 44 +++++++++++++++----------------- extensions/git/src/repository.ts | 21 +++++++++++++++ extensions/git/src/statusbar.ts | 3 ++- 3 files changed, 44 insertions(+), 24 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index dd5f2f9a029..c231c44ee94 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -5,7 +5,6 @@ import * as os from 'os'; import * as path from 'path'; -import * as picomatch from 'picomatch'; import { Command, commands, Disposable, LineChange, MessageOptions, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider, InputBoxValidationSeverity, TabInputText } from 'vscode'; import TelemetryReporter from '@vscode/extension-telemetry'; import * as nls from 'vscode-nls'; @@ -27,24 +26,25 @@ const localize = nls.loadMessageBundle(); class CheckoutItem implements QuickPickItem { protected get shortCommit(): string { return (this.ref.commit || '').substr(0, 8); } - get label(): string { return this.ref.name || this.shortCommit; } + get label(): string { return this.ref.name ? `${this.ref.name}${this.repository.isBranchProtected(this.ref.name) ? ' $(lock-small)' : ''}` : this.shortCommit; } get description(): string { return this.shortCommit; } - constructor(protected ref: Ref) { } + constructor(protected repository: Repository, protected ref: Ref) { } - async run(repository: Repository, opts?: { detached?: boolean }): Promise { + async run(opts?: { detached?: boolean }): Promise { const ref = this.ref.name; if (!ref) { return; } - await repository.checkout(ref, opts); + await this.repository.checkout(ref, opts); } } class CheckoutTagItem extends CheckoutItem { + override get label(): string { return this.ref.name || this.shortCommit; } override get description(): string { return localize('tag at', "Tag at {0}", this.shortCommit); } @@ -52,21 +52,22 @@ class CheckoutTagItem extends CheckoutItem { class CheckoutRemoteHeadItem extends CheckoutItem { + override get label(): string { return this.ref.name || this.shortCommit; } override get description(): string { return localize('remote branch at', "Remote branch at {0}", this.shortCommit); } - override async run(repository: Repository, opts?: { detached?: boolean }): Promise { + override async run(opts?: { detached?: boolean }): Promise { if (!this.ref.name) { return; } - const branches = await repository.findTrackingBranches(this.ref.name); + const branches = await this.repository.findTrackingBranches(this.ref.name); if (branches.length > 0) { - await repository.checkout(branches[0].name!, opts); + await this.repository.checkout(branches[0].name!, opts); } else { - await repository.checkoutTracking(this.ref.name, opts); + await this.repository.checkoutTracking(this.ref.name, opts); } } } @@ -219,7 +220,7 @@ function createCheckoutItems(repository: Repository): CheckoutItem[] { checkoutTypes = checkoutTypeConfig; } - const processors = checkoutTypes.map(getCheckoutProcessor) + const processors = checkoutTypes.map(type => getCheckoutProcessor(repository, type)) .filter(p => !!p) as CheckoutProcessor[]; for (const ref of repository.refs) { @@ -234,8 +235,8 @@ function createCheckoutItems(repository: Repository): CheckoutItem[] { class CheckoutProcessor { private refs: Ref[] = []; - get items(): CheckoutItem[] { return this.refs.map(r => new this.ctor(r)); } - constructor(private type: RefType, private ctor: { new(ref: Ref): CheckoutItem }) { } + get items(): CheckoutItem[] { return this.refs.map(r => new this.ctor(this.repository, r)); } + constructor(private repository: Repository, private type: RefType, private ctor: { new(repository: Repository, ref: Ref): CheckoutItem }) { } onRef(ref: Ref): void { if (ref.type === this.type) { @@ -244,14 +245,14 @@ class CheckoutProcessor { } } -function getCheckoutProcessor(type: string): CheckoutProcessor | undefined { +function getCheckoutProcessor(repository: Repository, type: string): CheckoutProcessor | undefined { switch (type) { case 'local': - return new CheckoutProcessor(RefType.Head, CheckoutItem); + return new CheckoutProcessor(repository, RefType.Head, CheckoutItem); case 'remote': - return new CheckoutProcessor(RefType.RemoteHead, CheckoutRemoteHeadItem); + return new CheckoutProcessor(repository, RefType.RemoteHead, CheckoutRemoteHeadItem); case 'tags': - return new CheckoutProcessor(RefType.Tag, CheckoutTagItem); + return new CheckoutProcessor(repository, RefType.Tag, CheckoutTagItem); } return undefined; @@ -1584,11 +1585,8 @@ export class CommandCenter { } // Branch protection - const branchProtection = config.get('branchProtection')!.map(bp => bp.trim()).filter(bp => bp !== ''); const branchProtectionPrompt = config.get<'alwaysCommit' | 'alwaysCommitToNewBranch' | 'alwaysPrompt'>('branchProtectionPrompt')!; - const branchIsProtected = branchProtection.some(bp => picomatch.isMatch(repository.HEAD?.name ?? '', bp)); - - if (branchIsProtected && (branchProtectionPrompt === 'alwaysPrompt' || branchProtectionPrompt === 'alwaysCommitToNewBranch')) { + if (repository.isBranchProtected() && (branchProtectionPrompt === 'alwaysPrompt' || branchProtectionPrompt === 'alwaysCommitToNewBranch')) { const commitToNewBranch = localize('commit to branch', "Commit to a New Branch"); let pick: string | undefined = commitToNewBranch; @@ -1859,7 +1857,7 @@ export class CommandCenter { const item = choice as CheckoutItem; try { - await item.run(repository, opts); + await item.run(opts); } catch (err) { if (err.gitErrorCode !== GitErrorCodes.DirtyWorkTree) { throw err; @@ -1871,10 +1869,10 @@ export class CommandCenter { if (choice === force) { await this.cleanAll(repository); - await item.run(repository, opts); + await item.run(opts); } else if (choice === stash) { await this.stash(repository); - await item.run(repository, opts); + await item.run(opts); await this.stashPopLatest(repository); } } diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 7a765bee11f..0190ce2c7be 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -5,6 +5,7 @@ import * as fs from 'fs'; import * as path from 'path'; +import * as picomatch from 'picomatch'; import { CancellationToken, Command, Disposable, Event, EventEmitter, Memento, ProgressLocation, ProgressOptions, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, ThemeColor, Uri, window, workspace, WorkspaceEdit, FileDecoration, commands, Tab, TabInputTextDiff, TabInputNotebookDiff, RelativePattern } from 'vscode'; import TelemetryReporter from '@vscode/extension-telemetry'; import * as nls from 'vscode-nls'; @@ -867,6 +868,7 @@ export class Repository implements Disposable { private isRepositoryHuge: false | { limit: number } = false; private didWarnAboutLimit = false; + private isBranchProtectedMatcher: picomatch.Matcher | undefined; private resourceCommandResolver = new ResourceCommandResolver(this); private disposables: Disposable[] = []; @@ -985,6 +987,10 @@ export class Repository implements Disposable { } }, null, this.disposables); + const onDidChangeBranchProtection = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.branchProtection', root)); + onDidChangeBranchProtection(this.updateBranchProtectionMatcher, this, this.disposables); + this.updateBranchProtectionMatcher(); + const statusBar = new StatusBarCommands(this, remoteSourcePublisherRegistry); this.disposables.push(statusBar); statusBar.onDidChange(() => this._sourceControl.statusBarCommands = statusBar.commands, null, this.disposables); @@ -2211,6 +2217,21 @@ export class Repository implements Disposable { } } + private updateBranchProtectionMatcher(): void { + const scopedConfig = workspace.getConfiguration('git', Uri.file(this.repository.root)); + const branchProtectionGlobs = scopedConfig.get('branchProtection')!.map(bp => bp.trim()).filter(bp => bp !== ''); + + if (branchProtectionGlobs.length === 0) { + this.isBranchProtectedMatcher = undefined; + } else { + this.isBranchProtectedMatcher = picomatch(branchProtectionGlobs); + } + } + + public isBranchProtected(name: string = this.HEAD?.name ?? ''): boolean { + return this.isBranchProtectedMatcher ? this.isBranchProtectedMatcher(name) : false; + } + dispose(): void { this.disposables = dispose(this.disposables); } diff --git a/extensions/git/src/statusbar.ts b/extensions/git/src/statusbar.ts index 7cf06e0ed4f..f5b2e3e87c8 100644 --- a/extensions/git/src/statusbar.ts +++ b/extensions/git/src/statusbar.ts @@ -24,7 +24,8 @@ class CheckoutStatusBar { get command(): Command | undefined { const rebasing = !!this.repository.rebaseCommit; - const title = `$(git-branch) ${this.repository.headLabel}${rebasing ? ` (${localize('rebasing', 'Rebasing')})` : ''}`; + const isBranchProtected = this.repository.isBranchProtected(); + const title = `$(git-branch) ${this.repository.headLabel}${rebasing ? ` (${localize('rebasing', 'Rebasing')})` : ''}${isBranchProtected ? ' $(lock-small)' : ''}`; return { command: 'git.checkout', From 88731bf6d68b97ee918b6afc13a79eed0d414106 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Thu, 16 Jun 2022 16:27:35 +0200 Subject: [PATCH 089/175] Git - enable using the editor as the git commit input (#152158) --- extensions/git/package.json | 8 ++++++- extensions/git/package.nls.json | 1 + extensions/git/src/askpass.ts | 33 ++++++++++++-------------- extensions/git/src/gitEditor.ts | 34 +++++++++++++-------------- extensions/git/src/ipc/ipcServer.ts | 9 ++++++-- extensions/git/src/main.ts | 14 +++++------ extensions/git/src/terminal.ts | 36 ++++++++++++++--------------- 7 files changed, 71 insertions(+), 64 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 603b8d62ed4..752304346ed 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -2033,7 +2033,7 @@ "type": "boolean", "scope": "resource", "description": "%config.useEditorAsCommitInput%", - "default": false + "default": true }, "git.verboseCommit": { "type": "boolean", @@ -2363,6 +2363,12 @@ "default": true, "description": "%config.terminalAuthentication%" }, + "git.terminalGitEditor": { + "type": "boolean", + "scope": "resource", + "default": true, + "description": "%config.terminalGitEditor%" + }, "git.useCommitInputAsStashMessage": { "type": "boolean", "scope": "resource", diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 5210e64de80..d17b31b71be 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -212,6 +212,7 @@ "config.requireGitUserConfig": "Controls whether to require explicit Git user configuration or allow Git to guess if missing.", "config.showCommitInput": "Controls whether to show the commit input in the Git source control panel.", "config.terminalAuthentication": "Controls whether to enable VS Code to be the authentication handler for git processes spawned in the integrated terminal. Note: terminals need to be restarted to pick up a change in this setting.", + "config.terminalGitEditor": "Controls whether to enable VS Code to be git editor for git processes spawned in the integrated terminal. Note: terminals need to be restarted to pick up a change in this setting.", "config.timeline.showAuthor": "Controls whether to show the commit author in the Timeline view.", "config.timeline.showUncommitted": "Controls whether to show uncommitted changes in the Timeline view.", "config.timeline.date": "Controls which date to use for items in the Timeline view.", diff --git a/extensions/git/src/askpass.ts b/extensions/git/src/askpass.ts index d9d9378f8aa..cbf981eea19 100644 --- a/extensions/git/src/askpass.ts +++ b/extensions/git/src/askpass.ts @@ -8,9 +8,11 @@ import { IDisposable, EmptyDisposable, toDisposable } from './util'; import * as path from 'path'; import { IIPCHandler, IIPCServer } from './ipc/ipcServer'; import { CredentialsProvider, Credentials } from './api/git'; +import { ITerminalEnvironmentProvider } from './terminal'; -export class Askpass implements IIPCHandler { +export class Askpass implements IIPCHandler, ITerminalEnvironmentProvider { + private env: { [key: string]: string }; private disposable: IDisposable = EmptyDisposable; private cache = new Map(); private credentialsProviders = new Set(); @@ -19,6 +21,13 @@ export class Askpass implements IIPCHandler { if (ipc) { this.disposable = ipc.registerHandler('askpass', this); } + + this.env = { + GIT_ASKPASS: path.join(__dirname, this.ipc ? 'askpass.sh' : 'askpass-empty.sh'), + VSCODE_GIT_ASKPASS_NODE: process.execPath, + VSCODE_GIT_ASKPASS_EXTRA_ARGS: (process.versions['electron'] && process.versions['microsoft-build']) ? '--ms-enable-electron-run-as-node' : '', + VSCODE_GIT_ASKPASS_MAIN: path.join(__dirname, 'askpass-main.js'), + }; } async handle({ request, host }: { request: string; host: string }): Promise { @@ -64,25 +73,13 @@ export class Askpass implements IIPCHandler { } getEnv(): { [key: string]: string } { - if (!this.ipc) { - return { - GIT_ASKPASS: path.join(__dirname, 'askpass-empty.sh') - }; - } - - const env: { [key: string]: string } = { - ...this.ipc.getEnv(), - VSCODE_GIT_ASKPASS_NODE: process.execPath, - VSCODE_GIT_ASKPASS_EXTRA_ARGS: (process.versions['electron'] && process.versions['microsoft-build']) ? '--ms-enable-electron-run-as-node' : '', - VSCODE_GIT_ASKPASS_MAIN: path.join(__dirname, 'askpass-main.js') - }; - const config = workspace.getConfiguration('git'); - if (config.get('useIntegratedAskPass')) { - env.GIT_ASKPASS = path.join(__dirname, 'askpass.sh'); - } + return config.get('useIntegratedAskPass') ? this.env : {}; + } - return env; + getTerminalEnv(): { [key: string]: string } { + const config = workspace.getConfiguration('git'); + return config.get('useIntegratedAskPass') && config.get('terminalAuthentication') ? this.env : {}; } registerCredentialsProvider(provider: CredentialsProvider): Disposable { diff --git a/extensions/git/src/gitEditor.ts b/extensions/git/src/gitEditor.ts index 0a2bb752afe..fb0688e4401 100644 --- a/extensions/git/src/gitEditor.ts +++ b/extensions/git/src/gitEditor.ts @@ -5,20 +5,29 @@ import * as path from 'path'; import { TabInputText, Uri, window, workspace } from 'vscode'; import { IIPCHandler, IIPCServer } from './ipc/ipcServer'; +import { ITerminalEnvironmentProvider } from './terminal'; import { EmptyDisposable, IDisposable } from './util'; interface GitEditorRequest { commitMessagePath?: string; } -export class GitEditor implements IIPCHandler { +export class GitEditor implements IIPCHandler, ITerminalEnvironmentProvider { + private env: { [key: string]: string }; private disposable: IDisposable = EmptyDisposable; - constructor(private ipc?: IIPCServer) { + constructor(ipc?: IIPCServer) { if (ipc) { this.disposable = ipc.registerHandler('git-editor', this); } + + this.env = { + GIT_EDITOR: `"${path.join(__dirname, ipc ? 'git-editor.sh' : 'git-editor-empty.sh')}"`, + VSCODE_GIT_EDITOR_NODE: process.execPath, + VSCODE_GIT_EDITOR_EXTRA_ARGS: (process.versions['electron'] && process.versions['microsoft-build']) ? '--ms-enable-electron-run-as-node' : '', + VSCODE_GIT_EDITOR_MAIN: path.join(__dirname, 'git-editor-main.js') + }; } async handle({ commitMessagePath }: GitEditorRequest): Promise { @@ -39,24 +48,13 @@ export class GitEditor implements IIPCHandler { } getEnv(): { [key: string]: string } { - if (!this.ipc) { - return { - GIT_EDITOR: `"${path.join(__dirname, 'git-editor-empty.sh')}"` - }; - } - - const env: { [key: string]: string } = { - VSCODE_GIT_EDITOR_NODE: process.execPath, - VSCODE_GIT_EDITOR_EXTRA_ARGS: (process.versions['electron'] && process.versions['microsoft-build']) ? '--ms-enable-electron-run-as-node' : '', - VSCODE_GIT_EDITOR_MAIN: path.join(__dirname, 'git-editor-main.js') - }; - const config = workspace.getConfiguration('git'); - if (config.get('useEditorAsCommitInput')) { - env.GIT_EDITOR = `"${path.join(__dirname, 'git-editor.sh')}"`; - } + return config.get('useEditorAsCommitInput') ? this.env : {}; + } - return env; + getTerminalEnv(): { [key: string]: string } { + const config = workspace.getConfiguration('git'); + return config.get('useEditorAsCommitInput') && config.get('terminalGitEditor') ? this.env : {}; } dispose(): void { diff --git a/extensions/git/src/ipc/ipcServer.ts b/extensions/git/src/ipc/ipcServer.ts index ad4583cbfe1..c9de357b1a6 100644 --- a/extensions/git/src/ipc/ipcServer.ts +++ b/extensions/git/src/ipc/ipcServer.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from 'vscode'; +import { ITerminalEnvironmentProvider } from '../terminal'; import { toDisposable } from '../util'; import * as path from 'path'; import * as http from 'http'; @@ -27,7 +28,7 @@ export interface IIPCHandler { handle(request: any): Promise; } -export async function createIPCServer(context?: string): Promise { +export async function createIPCServer(context?: string): Promise { const server = http.createServer(); const hash = crypto.createHash('sha1'); @@ -65,7 +66,7 @@ export interface IIPCServer extends Disposable { registerHandler(name: string, handler: IIPCHandler): Disposable; } -class IPCServer implements IIPCServer, Disposable { +export class IPCServer implements IIPCServer, ITerminalEnvironmentProvider, Disposable { private handlers = new Map(); get ipcHandlePath(): string { return this._ipcHandlePath; } @@ -110,6 +111,10 @@ class IPCServer implements IIPCServer, Disposable { return { VSCODE_GIT_IPC_HANDLE: this.ipcHandlePath }; } + getTerminalEnv(): { [key: string]: string } { + return { VSCODE_GIT_IPC_HANDLE: this.ipcHandlePath }; + } + dispose(): void { this.handlers.clear(); this.server.close(); diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 46f612539fb..6fbc88cc9fd 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -25,7 +25,7 @@ import { GitTimelineProvider } from './timelineProvider'; import { registerAPICommands } from './api/api1'; import { TerminalEnvironmentManager } from './terminal'; import { OutputChannelLogger } from './log'; -import { createIPCServer, IIPCServer } from './ipc/ipcServer'; +import { createIPCServer, IPCServer } from './ipc/ipcServer'; import { GitEditor } from './gitEditor'; const deactivateTasks: { (): Promise }[] = []; @@ -62,22 +62,22 @@ async function createModel(context: ExtensionContext, outputChannelLogger: Outpu return !skip; }); - let ipc: IIPCServer | undefined = undefined; + let ipcServer: IPCServer | undefined = undefined; try { - ipc = await createIPCServer(context.storagePath); + ipcServer = await createIPCServer(context.storagePath); } catch (err) { outputChannelLogger.logError(`Failed to create git IPC: ${err}`); } - const askpass = new Askpass(ipc); + const askpass = new Askpass(ipcServer); disposables.push(askpass); - const gitEditor = new GitEditor(ipc); + const gitEditor = new GitEditor(ipcServer); disposables.push(gitEditor); - const environment = { ...askpass.getEnv(), ...gitEditor.getEnv() }; - const terminalEnvironmentManager = new TerminalEnvironmentManager(context, environment); + const environment = { ...askpass.getEnv(), ...gitEditor.getEnv(), ...ipcServer?.getEnv() }; + const terminalEnvironmentManager = new TerminalEnvironmentManager(context, [askpass, gitEditor, ipcServer]); disposables.push(terminalEnvironmentManager); outputChannelLogger.logInfo(localize('using git', "Using git {0} from {1}", info.version, info.path)); diff --git a/extensions/git/src/terminal.ts b/extensions/git/src/terminal.ts index 2eda93151d2..9501cc88bba 100644 --- a/extensions/git/src/terminal.ts +++ b/extensions/git/src/terminal.ts @@ -6,27 +6,15 @@ import { ExtensionContext, workspace } from 'vscode'; import { filterEvent, IDisposable } from './util'; +export interface ITerminalEnvironmentProvider { + getTerminalEnv(): { [key: string]: string }; +} + export class TerminalEnvironmentManager { private readonly disposable: IDisposable; - private _enabled = false; - private set enabled(enabled: boolean) { - if (this._enabled === enabled) { - return; - } - - this._enabled = enabled; - this.context.environmentVariableCollection.clear(); - - if (enabled) { - for (const name of Object.keys(this.env)) { - this.context.environmentVariableCollection.replace(name, this.env[name]); - } - } - } - - constructor(private readonly context: ExtensionContext, private readonly env: { [key: string]: string }) { + constructor(private readonly context: ExtensionContext, private readonly envProviders: (ITerminalEnvironmentProvider | undefined)[]) { this.disposable = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git')) (this.refresh, this); @@ -35,7 +23,19 @@ export class TerminalEnvironmentManager { private refresh(): void { const config = workspace.getConfiguration('git', null); - this.enabled = config.get('enabled', true) && config.get('terminalAuthentication', true); + this.context.environmentVariableCollection.clear(); + + if (!config.get('enabled', true)) { + return; + } + + for (const envProvider of this.envProviders) { + const terminalEnv = envProvider?.getTerminalEnv() ?? {}; + + for (const name of Object.keys(terminalEnv)) { + this.context.environmentVariableCollection.replace(name, terminalEnv[name]); + } + } } dispose(): void { From 96e2f8cca5942fb39d56fcf1f82b1300b1016399 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Thu, 16 Jun 2022 16:29:36 +0200 Subject: [PATCH 090/175] Dispose the management connection when the extension host exits after running tests or after a development session to avoid reconnection attempts (#152334) Dispose the management connection when the extension host exits after running tests or after a development session to avoid reconnection attempts (#147755) --- .../extensions/electron-sandbox/electronExtensionService.ts | 6 ++++++ .../workbench/services/remote/common/remoteAgentService.ts | 1 + 2 files changed, 7 insertions(+) diff --git a/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts b/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts index 7b25b8f9142..db4ffbea65b 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts @@ -594,6 +594,12 @@ export abstract class ElectronExtensionService extends AbstractExtensionService // Dispose everything associated with the extension host this.stopExtensionHosts(); + // Dispose the management connection to avoid reconnecting after the extension host exits + const connection = this._remoteAgentService.getConnection(); + if (connection) { + connection.dispose(); + } + if (this._isExtensionDevTestFromCli) { // When CLI testing make sure to exit with proper exit code this._nativeHostService.exit(code); diff --git a/src/vs/workbench/services/remote/common/remoteAgentService.ts b/src/vs/workbench/services/remote/common/remoteAgentService.ts index 6a899e927be..0054090e83d 100644 --- a/src/vs/workbench/services/remote/common/remoteAgentService.ts +++ b/src/vs/workbench/services/remote/common/remoteAgentService.ts @@ -68,6 +68,7 @@ export interface IRemoteAgentConnection { readonly onReconnecting: Event; readonly onDidStateChange: Event; + dispose(): void; getChannel(channelName: string): T; withChannel(channelName: string, callback: (channel: T) => Promise): Promise; registerChannel>(channelName: string, channel: T): void; From 3fec83b84a0983e35e88c5c390301c19dc77e766 Mon Sep 17 00:00:00 2001 From: Leonardo Montini Date: Thu, 16 Jun 2022 16:29:55 +0200 Subject: [PATCH 091/175] Add command to toggle between light/dark color themes (#151554) * Added quick input action to toggle between preferred color themes * Removed quickpick in favor of command to toggle between ligh/dark and high-contrast counterparts * Moved toggle logic to themes.contribution * togglePreferredTheme -> toggleLightDarkThemes Co-authored-by: Leonardo Montini Co-authored-by: Martin Aeschlimann --- .../themes/browser/themes.contribution.ts | 49 ++++++++++++++++++- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts index a42283c1457..4e5f6525fb0 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts @@ -8,7 +8,7 @@ import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { MenuRegistry, MenuId, Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; import { CATEGORIES } from 'vs/workbench/common/actions'; -import { IWorkbenchThemeService, IWorkbenchTheme, ThemeSettingTarget, IWorkbenchColorTheme, IWorkbenchFileIconTheme, IWorkbenchProductIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IWorkbenchThemeService, IWorkbenchTheme, ThemeSettingTarget, IWorkbenchColorTheme, IWorkbenchFileIconTheme, IWorkbenchProductIconTheme, ThemeSettings } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { VIEWLET_ID, IExtensionsViewPaneContainer } from 'vs/workbench/contrib/extensions/common/extensions'; import { IExtensionGalleryService, IExtensionManagementService, IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IColorRegistry, Extensions as ColorRegistryExtensions } from 'vs/platform/theme/common/colorRegistry'; @@ -34,6 +34,7 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { FileIconThemeData } from 'vs/workbench/services/themes/browser/fileIconThemeData'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export const manageExtensionIcon = registerIcon('theme-selection-manage-extension', Codicon.gear, localize('manageExtensionIcon', 'Icon for the \'Manage\' action in the theme selection quick pick.')); @@ -583,6 +584,51 @@ registerAction2(class extends Action2 { } }); +const toggleLightDarkThemesCommandId = 'workbench.action.toggleLightDarkThemes'; + +registerAction2(class extends Action2 { + + constructor() { + super({ + id: toggleLightDarkThemesCommandId, + title: { value: localize('toggleLightDarkThemes.label', "Toggle between Light/Dark Themes"), original: 'Toggle between Light/Dark Themes' }, + category: CATEGORIES.Preferences, + f1: true, + }); + } + + override async run(accessor: ServicesAccessor) { + const themeService = accessor.get(IWorkbenchThemeService); + const configurationService = accessor.get(IConfigurationService); + + const currentTheme = themeService.getColorTheme(); + let newSettingsId: string = ThemeSettings.PREFERRED_DARK_THEME; + switch (currentTheme.type) { + case ColorScheme.LIGHT: + newSettingsId = ThemeSettings.PREFERRED_DARK_THEME; + break; + case ColorScheme.DARK: + newSettingsId = ThemeSettings.PREFERRED_LIGHT_THEME; + break; + case ColorScheme.HIGH_CONTRAST_LIGHT: + newSettingsId = ThemeSettings.PREFERRED_HC_DARK_THEME; + break; + case ColorScheme.HIGH_CONTRAST_DARK: + newSettingsId = ThemeSettings.PREFERRED_HC_LIGHT_THEME; + break; + } + + const themeSettingId: string = configurationService.getValue(newSettingsId); + + if (themeSettingId && typeof themeSettingId === 'string') { + const theme = (await themeService.getColorThemes()).find(t => t.settingsId === themeSettingId); + if (theme) { + themeService.setColorTheme(theme.id, 'auto'); + } + } + } +}); + MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { group: '4_themes', command: { @@ -610,7 +656,6 @@ MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { order: 3 }); - MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { group: '4_themes', command: { From 49ef85f3a52ae2aed539adca304ea547f55944ce Mon Sep 17 00:00:00 2001 From: Robo Date: Thu, 16 Jun 2022 23:34:09 +0900 Subject: [PATCH 092/175] fix: spawn calls from utility extension host (#152319) --- src/vs/workbench/api/node/extensionHostProcess.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vs/workbench/api/node/extensionHostProcess.ts b/src/vs/workbench/api/node/extensionHostProcess.ts index 0d111d2f20a..331b384e394 100644 --- a/src/vs/workbench/api/node/extensionHostProcess.ts +++ b/src/vs/workbench/api/node/extensionHostProcess.ts @@ -90,6 +90,12 @@ function patchProcess(allowExit: boolean) { const err = new Error('An extension called process.crash() and this was prevented.'); console.warn(err.stack); }; + + // Set ELECTRON_RUN_AS_NODE environment variable for extensions that use + // child_process.spawn with process.execPath and expect to run as node process + // on the desktop. + // Refs https://github.com/microsoft/vscode/issues/151012#issuecomment-1156593228 + process.env['ELECTRON_RUN_AS_NODE'] = '1'; } interface IRendererConnection { From 0547fdbe65cf82089f847607ba9e7111b7594426 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Thu, 16 Jun 2022 16:42:28 +0200 Subject: [PATCH 093/175] Avoid installing playwright (#152335) --- .github/workflows/basic.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/basic.yml b/.github/workflows/basic.yml index 3161158f947..025455a1afb 100644 --- a/.github/workflows/basic.yml +++ b/.github/workflows/basic.yml @@ -62,7 +62,7 @@ jobs: run: yarn --frozen-lockfile --network-timeout 180000 - name: Compile and Download - run: yarn npm-run-all --max_old_space_size=4095 -lp compile "electron x64" playwright-install download-builtin-extensions + run: yarn npm-run-all --max_old_space_size=4095 -lp compile "electron x64" download-builtin-extensions - name: Run Unit Tests id: electron-unit-tests @@ -114,9 +114,6 @@ jobs: ELECTRON_SKIP_BINARY_DOWNLOAD: 1 run: yarn --frozen-lockfile --network-timeout 180000 - - name: Download Playwright - run: yarn playwright-install - - name: Run Hygiene Checks run: yarn gulp hygiene From 44713a6ce2abf2f9205f0885f67cde54d34b3fde Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 16 Jun 2022 17:02:13 +0200 Subject: [PATCH 094/175] Surface profile actions (#152332) Surface profile actions in - settings gear - accounts - preferences --- .../userDataProfile/common/userDataProfile.ts | 9 + .../electron-main/userDataProfile.ts | 27 +-- .../electron-sandbox/userDataProfile.ts | 11 ++ .../browser/userDataProfile.contribution.ts | 36 +--- .../browser/userDataProfile.ts | 119 +++++++++++++ .../common/userDataProfileActions.ts | 165 ++++++++++-------- .../browser/userDataProfileManagement.ts | 29 ++- .../userDataProfile/common/userDataProfile.ts | 12 +- 8 files changed, 275 insertions(+), 133 deletions(-) create mode 100644 src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts diff --git a/src/vs/platform/userDataProfile/common/userDataProfile.ts b/src/vs/platform/userDataProfile/common/userDataProfile.ts index c1e32808e7f..5db34ee2f99 100644 --- a/src/vs/platform/userDataProfile/common/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/common/userDataProfile.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { hash } from 'vs/base/common/hash'; +import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { joinPath } from 'vs/base/common/resources'; import { UriDto } from 'vs/base/common/types'; @@ -54,6 +55,9 @@ export interface IUserDataProfilesService { readonly defaultProfile: IUserDataProfile; readonly currentProfile: IUserDataProfile; + readonly onDidChangeProfiles: Event; + readonly profiles: IUserDataProfile[]; + newProfile(name: string, options?: ProfileOptions): IUserDataProfile; createProfile(profile: IUserDataProfile, options: ProfileOptions, workspaceIdentifier?: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise; setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise; @@ -88,6 +92,11 @@ export class UserDataProfilesService extends Disposable implements IUserDataProf protected _defaultProfile: IUserDataProfile; get defaultProfile(): IUserDataProfile { return this._defaultProfile; } + get profiles(): IUserDataProfile[] { return []; } + + protected readonly _onDidChangeProfiles = this._register(new Emitter()); + readonly onDidChangeProfiles = this._onDidChangeProfiles.event; + constructor( defaultProfile: UriDto | undefined, currentProfile: UriDto | undefined, diff --git a/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts b/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts index 3340b69c350..7b1095ebd52 100644 --- a/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts @@ -15,7 +15,7 @@ import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity' import { ProfileOptions, DefaultOptions, IUserDataProfile, IUserDataProfilesService, UserDataProfilesService, reviveProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; -type UserDataProfiles = { +type UserDataProfilesObject = { profiles: IUserDataProfile[]; workspaces: ResourceMap; }; @@ -52,9 +52,9 @@ export class UserDataProfilesMainService extends UserDataProfilesService impleme } } - private _profiles: UserDataProfiles | undefined; - private get profiles(): UserDataProfiles { - if (!this._profiles) { + private _profilesObject: UserDataProfilesObject | undefined; + private get profilesObject(): UserDataProfilesObject { + if (!this._profilesObject) { const profiles = this.storedProfiles.map(storedProfile => this.toUserDataProfile(storedProfile.name, storedProfile.location, storedProfile.options, this.defaultProfile)); profiles.unshift(this.defaultProfile); const workspaces = this.storedWorskpaceInfos.reduce((workspaces, workspaceProfileInfo) => { @@ -64,17 +64,19 @@ export class UserDataProfilesMainService extends UserDataProfilesService impleme } return workspaces; }, new ResourceMap()); - this._profiles = { profiles: profiles, workspaces: workspaces }; + this._profilesObject = { profiles: profiles, workspaces: workspaces }; } - return this._profiles; + return this._profilesObject; } + override get profiles(): IUserDataProfile[] { return this.profilesObject.profiles; } + override async getAllProfiles(): Promise { - return this.profiles.profiles; + return this.profiles; } override getProfile(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): IUserDataProfile { - return this.profiles.workspaces.get(this.getWorkspace(workspaceIdentifier)) ?? this.defaultProfile; + return this.profilesObject.workspaces.get(this.getWorkspace(workspaceIdentifier)) ?? this.defaultProfile; } override async createProfile(profile: IUserDataProfile, options: ProfileOptions, workspaceIdentifier?: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise { @@ -88,7 +90,7 @@ export class UserDataProfilesMainService extends UserDataProfilesService impleme if (workspaceIdentifier) { await this.setProfileForWorkspace(profile, workspaceIdentifier); } - return this.profiles.profiles.find(p => this.uriIdentityService.extUri.isEqual(p.location, profile.location))!; + return this.profilesObject.profiles.find(p => this.uriIdentityService.extUri.isEqual(p.location, profile.location))!; } override async setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise { @@ -99,7 +101,7 @@ export class UserDataProfilesMainService extends UserDataProfilesService impleme storedWorkspaceInfos.push({ workspace, profile: profile.location }); } this.storedWorskpaceInfos = storedWorkspaceInfos; - return this.profiles.profiles.find(p => this.uriIdentityService.extUri.isEqual(p.location, profile.location))!; + return this.profilesObject.profiles.find(p => this.uriIdentityService.extUri.isEqual(p.location, profile.location))!; } private getWorkspace(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier) { @@ -124,7 +126,8 @@ export class UserDataProfilesMainService extends UserDataProfilesService impleme private set storedProfiles(storedProfiles: StoredUserDataProfile[]) { this.stateMainService.setItem(UserDataProfilesMainService.PROFILES_KEY, storedProfiles); - this._profiles = undefined; + this._profilesObject = undefined; + this._onDidChangeProfiles.fire(this.profiles); } private get storedWorskpaceInfos(): StoredWorkspaceInfo[] { @@ -133,7 +136,7 @@ export class UserDataProfilesMainService extends UserDataProfilesService impleme private set storedWorskpaceInfos(storedWorkspaceInfos: StoredWorkspaceInfo[]) { this.stateMainService.setItem(UserDataProfilesMainService.WORKSPACE_PROFILE_INFO_KEY, storedWorkspaceInfos); - this._profiles = undefined; + this._profilesObject = undefined; } } diff --git a/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts b/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts index d2bcfd82043..b90fc159285 100644 --- a/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts @@ -13,6 +13,9 @@ import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platf export class UserDataProfilesNativeService extends UserDataProfilesService implements IUserDataProfilesService { + private _profiles: IUserDataProfile[] = []; + override get profiles(): IUserDataProfile[] { return this._profiles; } + constructor( defaultProfile: IUserDataProfile, currentProfile: IUserDataProfile, @@ -22,6 +25,14 @@ export class UserDataProfilesNativeService extends UserDataProfilesService imple @ILogService logService: ILogService, ) { super(defaultProfile, currentProfile, environmentService, fileService, logService); + this.getAllProfiles().then(profiles => { + this._profiles = profiles; + this._onDidChangeProfiles.fire(this._profiles); + this._register(this.channel.listen('onDidChangeProfiles')((profiles) => { + this._profiles = profiles; + this._onDidChangeProfiles.fire(this._profiles); + })); + }); } override async createProfile(profile: IUserDataProfile, options: ProfileOptions, workspaceIdentifier?: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise { diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.contribution.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.contribution.ts index e4e9946c3c8..5e9683717a0 100644 --- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.contribution.ts +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.contribution.ts @@ -3,42 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from 'vs/base/common/lifecycle'; -import { localize } from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions } from 'vs/workbench/common/contributions'; +import { IWorkbenchContributionsRegistry, Extensions } from 'vs/workbench/common/contributions'; +import { UserDataProfilesWorkbenchContribution } from 'vs/workbench/contrib/userDataProfile/browser/userDataProfile'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar'; -import { PROFILES_CATEGORY } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import '../common/profileActions'; import '../common/userDataProfileActions'; -class UserDataProfileStatusBarEntryContribution extends Disposable implements IWorkbenchContribution { - - constructor( - @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, - @IStatusbarService private readonly statusBarService: IStatusbarService, - @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, - ) { - super(); - this.updateStatus(); - this._register(this.workspaceContextService.onDidChangeWorkbenchState(() => this.updateStatus())); - } - - private async updateStatus(): Promise { - const profiles = await this.userDataProfilesService.getAllProfiles(); - if (profiles.length > 1 && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY) { - this.statusBarService.addEntry({ - name: this.userDataProfilesService.currentProfile.name!, - command: 'workbench.profiles.actions.switchProfile', - ariaLabel: localize('currentProfile', "Current Settings Profile is {0}", this.userDataProfilesService.currentProfile.name), - text: `${PROFILES_CATEGORY}: ${this.userDataProfilesService.currentProfile.name!}`, - }, 'status.userDataProfile', StatusbarAlignment.LEFT, 1); - } - } -} - const workbenchRegistry = Registry.as(Extensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(UserDataProfileStatusBarEntryContribution, LifecyclePhase.Eventually); +workbenchRegistry.registerWorkbenchContribution(UserDataProfilesWorkbenchContribution, LifecyclePhase.Ready); diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts new file mode 100644 index 00000000000..8aeaa0c7402 --- /dev/null +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts @@ -0,0 +1,119 @@ +/*--------------------------------------------------------------------------------------------- + * 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, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; +import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +import { localize } from 'vs/nls'; +import { Action2, ISubmenuItem, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; +import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IsDevelopmentContext } from 'vs/platform/contextkey/common/contextkeys'; +import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { WorkbenchStateContext } from 'vs/workbench/common/contextkeys'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar'; +import { IUserDataProfileManagementService, ManageProfilesSubMenu, PROFILES_CATEGORY, PROFILES_TTILE } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; + +const CONTEXT_CURRENT_PROFILE = new RawContextKey('currentUserDataProfile', ''); + +export class UserDataProfilesWorkbenchContribution extends Disposable implements IWorkbenchContribution { + + private readonly currentProfileContext: IContextKey; + + constructor( + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, + @IUserDataProfileManagementService private readonly userDataProfileManagementService: IUserDataProfileManagementService, + @IStatusbarService private readonly statusBarService: IStatusbarService, + @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, + @IContextKeyService contextKeyService: IContextKeyService, + ) { + super(); + + this.currentProfileContext = CONTEXT_CURRENT_PROFILE.bindTo(contextKeyService); + this.currentProfileContext.set(this.userDataProfilesService.currentProfile.id); + + this.updateStatus(); + this._register(this.workspaceContextService.onDidChangeWorkbenchState(() => this.updateStatus())); + + this.registerActions(); + } + + private registerActions(): void { + this.registerManageProfilesSubMenu(); + + this.registerProfilesActions(); + this._register(this.userDataProfilesService.onDidChangeProfiles(() => this.registerProfilesActions())); + } + + private registerManageProfilesSubMenu(): void { + const that = this; + const when = ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')); + MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { + get title() { return localize('manageProfiles', "{0} ({1})", PROFILES_TTILE.value, that.userDataProfilesService.currentProfile.name); }, + submenu: ManageProfilesSubMenu, + group: '5_profiles', + when, + order: 3 + }); + MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { + title: PROFILES_TTILE, + submenu: ManageProfilesSubMenu, + group: '5_profiles', + when, + order: 3 + }); + MenuRegistry.appendMenuItem(MenuId.AccountsContext, { + get title() { return localize('manageProfiles', "{0} ({1})", PROFILES_TTILE.value, that.userDataProfilesService.currentProfile.name); }, + submenu: ManageProfilesSubMenu, + group: '1_profiles', + when, + }); + } + + private readonly profilesDisposable = this._register(new MutableDisposable()); + private registerProfilesActions(): void { + this.profilesDisposable.value = new DisposableStore(); + for (const profile of this.userDataProfilesService.profiles) { + this.profilesDisposable.value.add(this.registerProfileEntryAction(profile)); + } + } + + private registerProfileEntryAction(profile: IUserDataProfile): IDisposable { + const that = this; + return registerAction2(class ProfileEntryAction extends Action2 { + constructor() { + const when = ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')); + super({ + id: `workbench.profiles.actions.profileEntry.${profile.id}`, + title: profile.name, + toggled: ContextKeyExpr.equals(CONTEXT_CURRENT_PROFILE.key, profile.id), + precondition: ContextKeyExpr.notEquals(CONTEXT_CURRENT_PROFILE.key, profile.id), + menu: [ + { + id: ManageProfilesSubMenu, + group: '0_profiles', + when, + } + ] + }); + } + async run(accessor: ServicesAccessor) { + return that.userDataProfileManagementService.switchProfile(profile); + } + }); + } + + private async updateStatus(): Promise { + const profiles = await this.userDataProfilesService.getAllProfiles(); + if (profiles.length > 1 && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY) { + this.statusBarService.addEntry({ + name: this.userDataProfilesService.currentProfile.name!, + command: 'workbench.profiles.actions.switchProfile', + ariaLabel: localize('currentProfile', "Current Settings Profile is {0}", this.userDataProfilesService.currentProfile.name), + text: `${PROFILES_CATEGORY}: ${this.userDataProfilesService.currentProfile.name!}`, + }, 'status.userDataProfile', StatusbarAlignment.LEFT, 1); + } + } +} diff --git a/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts b/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts index 1c6f6b1ecf0..e4db0c6926b 100644 --- a/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts +++ b/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts @@ -14,26 +14,35 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { INotificationService } from 'vs/platform/notification/common/notification'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { asJson, asText, IRequestService } from 'vs/platform/request/common/request'; -import { IUserDataProfileTemplate, isProfile, IUserDataProfileManagementService, IUserDataProfileWorkbenchService, PROFILES_CATEGORY, PROFILE_EXTENSION, PROFILE_FILTER } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; +import { IUserDataProfileTemplate, isProfile, IUserDataProfileManagementService, IUserDataProfileWorkbenchService, PROFILES_CATEGORY, PROFILE_EXTENSION, PROFILE_FILTER, ManageProfilesSubMenu } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IsDevelopmentContext } from 'vs/platform/contextkey/common/contextkeys'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { WorkbenchStateContext } from 'vs/workbench/common/contextkeys'; import { CATEGORIES } from 'vs/workbench/common/actions'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; -registerAction2(class SaveProfileAsAction extends Action2 { +registerAction2(class CreateFromCurrentProfileAction extends Action2 { constructor() { + const when = ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')); super({ - id: 'workbench.profiles.actions.saveProfileAs', + id: 'workbench.profiles.actions.createFromCurrentProfile', title: { - value: localize('save profile as', "Save Settings Profile As..."), - original: 'Save Settings Profile As...' + value: localize('save profile as', "Create from Current Profile..."), + original: 'Create from Current Profile...' }, category: PROFILES_CATEGORY, f1: true, - precondition: ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')), + precondition: when, + menu: [ + { + id: ManageProfilesSubMenu, + group: '1_create_profiles', + when, + order: 1 + } + ] }); } @@ -42,7 +51,7 @@ registerAction2(class SaveProfileAsAction extends Action2 { const userDataProfileManagementService = accessor.get(IUserDataProfileManagementService); const name = await quickInputService.input({ placeHolder: localize('name', "Profile name"), - title: localize('save profile as', "Save Settings Profile As..."), + title: localize('save profile as', "Create from Current Profile..."), }); if (name) { await userDataProfileManagementService.createAndEnterProfile(name, undefined, true); @@ -50,6 +59,79 @@ registerAction2(class SaveProfileAsAction extends Action2 { } }); +registerAction2(class CreateEmptyProfileAction extends Action2 { + constructor() { + const when = ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')); + super({ + id: 'workbench.profiles.actions.createProfile', + title: { + value: localize('create profile', "Create an Empty Profile..."), + original: 'Create an Empty Profile...' + }, + category: PROFILES_CATEGORY, + f1: true, + precondition: when, + menu: [ + { + id: ManageProfilesSubMenu, + group: '1_create_profiles', + when, + order: 2 + } + ] + }); + } + + async run(accessor: ServicesAccessor) { + const quickInputService = accessor.get(IQuickInputService); + const userDataProfileManagementService = accessor.get(IUserDataProfileManagementService); + const name = await quickInputService.input({ + placeHolder: localize('name', "Profile name"), + title: localize('create and enter empty profile', "Create an Empty Profile..."), + }); + if (name) { + await userDataProfileManagementService.createAndEnterProfile(name); + } + } +}); + +registerAction2(class RemoveProfileAction extends Action2 { + constructor() { + const when = ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')); + super({ + id: 'workbench.profiles.actions.removeProfile', + title: { + value: localize('remove profile', "Remove Profile..."), + original: 'Remove Profile...' + }, + category: PROFILES_CATEGORY, + f1: true, + precondition: when, + menu: [ + { + id: ManageProfilesSubMenu, + group: '2_manage_profiles', + when + } + ] + }); + } + + async run(accessor: ServicesAccessor) { + const quickInputService = accessor.get(IQuickInputService); + const userDataProfilesService = accessor.get(IUserDataProfilesService); + const userDataProfileManagementService = accessor.get(IUserDataProfileManagementService); + + const profiles = (await userDataProfilesService.getAllProfiles()).filter(p => p.name !== userDataProfilesService.currentProfile.name && p.name !== userDataProfilesService.defaultProfile.name); + if (profiles.length) { + const pick = await quickInputService.pick(profiles.map(profile => ({ label: profile.name, profile })), { placeHolder: localize('pick profile', "Select Settings Profile") }); + if (pick) { + await userDataProfileManagementService.removeProfile(pick.profile); + } + } + } +}); + registerAction2(class SwitchProfileAction extends Action2 { constructor() { super({ @@ -71,42 +153,14 @@ registerAction2(class SwitchProfileAction extends Action2 { const profiles = await userDataProfilesService.getAllProfiles(); if (profiles.length) { - const picks: IQuickPickItem[] = profiles.map(p => ({ - label: p.name!, - description: p.name === userDataProfilesService.currentProfile.name ? localize('current', "Current") : undefined, + const picks: Array = profiles.map(profile => ({ + label: profile.name!, + description: profile.name === userDataProfilesService.currentProfile.name ? localize('current', "Current") : undefined, + profile })); const pick = await quickInputService.pick(picks, { placeHolder: localize('pick profile', "Select Settings Profile") }); if (pick) { - await userDataProfileManagementService.switchProfile(pick.label); - } - } - } -}); - -registerAction2(class RemoveProfileAction extends Action2 { - constructor() { - super({ - id: 'workbench.profiles.actions.removeProfile', - title: { - value: localize('remove profile', "Remove Settings Profile"), - original: 'Remove Settings Profile' - }, - category: PROFILES_CATEGORY, - f1: true, - precondition: ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')), - }); - } - - async run(accessor: ServicesAccessor) { - const quickInputService = accessor.get(IQuickInputService); - const userDataProfilesService = accessor.get(IUserDataProfilesService); - const userDataProfileManagementService = accessor.get(IUserDataProfileManagementService); - - const profiles = (await userDataProfilesService.getAllProfiles()).filter(p => p.name !== userDataProfilesService.currentProfile.name && p.name !== userDataProfilesService.defaultProfile.name); - if (profiles.length) { - const pick = await quickInputService.pick(profiles.map(p => ({ label: p.name! })), { placeHolder: localize('pick profile', "Select Settings Profile") }); - if (pick) { - await userDataProfileManagementService.removeProfile(pick.label); + await userDataProfileManagementService.switchProfile(pick.profile); } } } @@ -138,33 +192,6 @@ registerAction2(class CleanupProfilesAction extends Action2 { } }); -registerAction2(class CreateAndEnterEmptyProfileAction extends Action2 { - constructor() { - super({ - id: 'workbench.profiles.actions.createAndEnterEmptyProfile', - title: { - value: localize('create and enter empty profile', "Create and Enter Empty Profile..."), - original: 'Create and Enter Empty Profile...' - }, - category: PROFILES_CATEGORY, - f1: true, - precondition: ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')), - }); - } - - async run(accessor: ServicesAccessor) { - const quickInputService = accessor.get(IQuickInputService); - const userDataProfileManagementService = accessor.get(IUserDataProfileManagementService); - const name = await quickInputService.input({ - placeHolder: localize('name', "Profile name"), - title: localize('create and enter empty profile', "Create and Enter Empty Profile..."), - }); - if (name) { - await userDataProfileManagementService.createAndEnterProfile(name); - } - } -}); - registerAction2(class ExportProfileAction extends Action2 { constructor() { super({ @@ -242,7 +269,7 @@ registerAction2(class ImportProfileAction extends Action2 { if (profile) { const name = await quickInputService.input({ placeHolder: localize('name', "Profile name"), - title: localize('save profile as', "Save Settings Profile As..."), + title: localize('save profile as', "Create from Current Profile..."), }); if (name) { await userDataProfileMangementService.createAndEnterProfileFromTemplate(name, profile); diff --git a/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts b/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts index 83afead166e..954fe75024c 100644 --- a/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts +++ b/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts @@ -16,7 +16,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceContextService, IWorkspaceIdentifier, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IExtensionManagementServerService, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IHostService } from 'vs/workbench/services/host/browser/host'; @@ -81,41 +81,40 @@ export class UserDataProfileManagementService extends Disposable implements IUse } } else { promises.push(this.fileService.createFolder(newProfile.globalStorageHome)); + if (!this.userDataProfilesService.defaultProfile.extensionsResource) { + promises.push(this.createDefaultExtensionsProfile(joinPath(this.userDataProfilesService.defaultProfile.location, basename(newProfile.extensionsResource!)))); + } } await Promise.allSettled(promises); await this.userDataProfilesService.createProfile(newProfile, options, workspaceIdentifier); await this.enterProfile(); } - async removeProfile(name: string): Promise { - if (name === this.userDataProfilesService.defaultProfile.name) { + async removeProfile(profile: IUserDataProfile): Promise { + if (!this.userDataProfilesService.profiles.some(p => p.id === profile.id)) { + throw new Error(`Profile ${profile.name} does not exist`); + } + if (profile.isDefault) { throw new Error(localize('cannotDeleteDefaultProfile', "Cannot delete the default profile")); } - if (name === this.userDataProfilesService.currentProfile.name) { + if (profile.id === this.userDataProfilesService.currentProfile.id) { throw new Error(localize('cannotDeleteCurrentProfile', "Cannot delete the current profile")); } - const profiles = await this.userDataProfilesService.getAllProfiles(); - const profile = profiles.find(p => p.name === name); - if (!profile) { - throw new Error(`Profile ${name} does not exist`); - } await this.userDataProfilesService.removeProfile(profile); - if (profiles.length === 2) { + if (this.userDataProfilesService.profiles.length === 2) { await this.fileService.del(this.userDataProfilesService.profilesHome, { recursive: true }); } else { await this.fileService.del(profile.location, { recursive: true }); } } - async switchProfile(name: string): Promise { + async switchProfile(profile: IUserDataProfile): Promise { const workspaceIdentifier = this.getWorkspaceIdentifier(); if (!workspaceIdentifier) { throw new Error(localize('cannotSwitchProfileInEmptyWorkbench', "Cannot switch a profile in an empty workspace")); } - const profiles = await this.userDataProfilesService.getAllProfiles(); - const profile = profiles.find(p => p.name === name); - if (!profile) { - throw new Error(`Profile ${name} does not exist`); + if (!this.userDataProfilesService.profiles.some(p => p.id === profile.id)) { + throw new Error(`Profile ${profile.name} does not exist`); } await this.userDataProfilesService.setProfileForWorkspace(profile, workspaceIdentifier); await this.enterProfile(); diff --git a/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts b/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts index a63d81994c0..154581f41a9 100644 --- a/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts +++ b/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts @@ -5,7 +5,9 @@ import { isUndefined } from 'vs/base/common/types'; import { localize } from 'vs/nls'; +import { MenuId } from 'vs/platform/actions/common/actions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; export type CreationOptions = { settings?: boolean; @@ -22,8 +24,8 @@ export interface IUserDataProfileManagementService { createAndEnterProfile(name: string, options?: CreationOptions, fromExisting?: boolean): Promise; createAndEnterProfileFromTemplate(name: string, template: IUserDataProfileTemplate, options?: CreationOptions): Promise; - removeProfile(name: string): Promise; - switchProfile(name: string): Promise; + removeProfile(profile: IUserDataProfile): Promise; + switchProfile(profile: IUserDataProfile): Promise; } @@ -59,6 +61,8 @@ export interface IResourceProfile { applyProfile(content: string): Promise; } -export const PROFILES_CATEGORY = localize('settings profiles', "Settings Profile"); +export const ManageProfilesSubMenu = new MenuId('Profiles'); +export const PROFILES_TTILE = { value: localize('settings profiles', "Profiles"), original: 'Profiles' }; +export const PROFILES_CATEGORY = PROFILES_TTILE.value; export const PROFILE_EXTENSION = 'code-profile'; -export const PROFILE_FILTER = [{ name: localize('profile', "Settings Profile"), extensions: [PROFILE_EXTENSION] }]; +export const PROFILE_FILTER = [{ name: localize('profile', "Profile"), extensions: [PROFILE_EXTENSION] }]; From 8fa5722aad72c6712c821bc1deeede56966b5d84 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 16 Jun 2022 17:02:50 +0200 Subject: [PATCH 095/175] Add openTunnel embedder API (#152336) --- src/vs/platform/tunnel/common/tunnel.ts | 15 ++++++++++ .../api/node/extHostTunnelService.ts | 19 ++----------- src/vs/workbench/browser/web.api.ts | 10 +++++++ src/vs/workbench/browser/web.main.ts | 28 +++++++++++++++++-- 4 files changed, 54 insertions(+), 18 deletions(-) diff --git a/src/vs/platform/tunnel/common/tunnel.ts b/src/vs/platform/tunnel/common/tunnel.ts index 827ec9bd42f..29b07680481 100644 --- a/src/vs/platform/tunnel/common/tunnel.ts +++ b/src/vs/platform/tunnel/common/tunnel.ts @@ -163,6 +163,21 @@ export function isPortPrivileged(port: number, os?: OperatingSystem): boolean { } } +export class DisposableTunnel { + private _onDispose: Emitter = new Emitter(); + onDidDispose: Event = this._onDispose.event; + + constructor( + public readonly remoteAddress: { port: number; host: string }, + public readonly localAddress: { port: number; host: string } | string, + private readonly _dispose: () => Promise) { } + + dispose(): Promise { + this._onDispose.fire(); + return this._dispose(); + } +} + export abstract class AbstractTunnelService implements ITunnelService { declare readonly _serviceBrand: undefined; diff --git a/src/vs/workbench/api/node/extHostTunnelService.ts b/src/vs/workbench/api/node/extHostTunnelService.ts index 4769e6f194c..d7abe11a16d 100644 --- a/src/vs/workbench/api/node/extHostTunnelService.ts +++ b/src/vs/workbench/api/node/extHostTunnelService.ts @@ -16,27 +16,14 @@ import * as pfs from 'vs/base/node/pfs'; import * as types from 'vs/workbench/api/common/extHostTypes'; import { isLinux } from 'vs/base/common/platform'; import { IExtHostTunnelService, TunnelDtoConverter } from 'vs/workbench/api/common/extHostTunnelService'; -import { Event, Emitter } from 'vs/base/common/event'; -import { TunnelOptions, TunnelCreationOptions, ProvidedPortAttributes, ProvidedOnAutoForward, isLocalhost, isAllInterfaces } from 'vs/platform/tunnel/common/tunnel'; +import { Emitter } from 'vs/base/common/event'; +import { TunnelOptions, TunnelCreationOptions, ProvidedPortAttributes, ProvidedOnAutoForward, isLocalhost, isAllInterfaces, DisposableTunnel } from 'vs/platform/tunnel/common/tunnel'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { MovingAverage } from 'vs/base/common/numbers'; import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplorerService'; import { ILogService } from 'vs/platform/log/common/log'; -class ExtensionTunnel implements vscode.Tunnel { - private _onDispose: Emitter = new Emitter(); - onDidDispose: Event = this._onDispose.event; - - constructor( - public readonly remoteAddress: { port: number; host: string }, - public readonly localAddress: { port: number; host: string } | string, - private readonly _dispose: () => Promise) { } - - dispose(): Promise { - this._onDispose.fire(); - return this._dispose(); - } -} +class ExtensionTunnel extends DisposableTunnel implements vscode.Tunnel { } export function getSockets(stdout: string): Record { const lines = stdout.trim().split('\n'); diff --git a/src/vs/workbench/browser/web.api.ts b/src/vs/workbench/browser/web.api.ts index f41ce51c118..4eddbcbba88 100644 --- a/src/vs/workbench/browser/web.api.ts +++ b/src/vs/workbench/browser/web.api.ts @@ -110,6 +110,16 @@ export interface IWorkbench { * has been persisted. */ shutdown: () => Promise; + + /** + * Forwards a port. If the current embedder implements a tunnelFactory then that will be used to make the tunnel. + * By default, openTunnel only support localhost; however, a tunnelFactory can be used to support other ips. + * + * @throws When run in an environment without a remote. + * + * @param tunnelOptions The `localPort` is a suggestion only. If that port is not available another will be chosen. + */ + openTunnel(tunnelOptions: ITunnelOptions): Thenable; } export interface IWorkbenchConstructionOptions { diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 5ebd48271c0..ec36613901f 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -31,7 +31,7 @@ import { WorkspaceService } from 'vs/workbench/services/configuration/browser/co import { ConfigurationCache } from 'vs/workbench/services/configuration/common/configurationCache'; import { ISignService } from 'vs/platform/sign/common/sign'; import { SignService } from 'vs/platform/sign/browser/signService'; -import { IWorkbenchConstructionOptions, IWorkbench } from 'vs/workbench/browser/web.api'; +import { IWorkbenchConstructionOptions, IWorkbench, ITunnel } from 'vs/workbench/browser/web.api'; import { BrowserStorageService } from 'vs/platform/storage/browser/storageService'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { BufferLogService } from 'vs/platform/log/common/bufferLog'; @@ -75,6 +75,9 @@ import { DelayedLogChannel } from 'vs/workbench/services/output/common/delayedLo import { dirname, joinPath } from 'vs/base/common/resources'; import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { NullPolicyService } from 'vs/platform/policy/common/policy'; +import { IRemoteExplorerService, TunnelSource } from 'vs/workbench/services/remote/common/remoteExplorerService'; +import { DisposableTunnel } from 'vs/platform/tunnel/common/tunnel'; +import { ILabelService } from 'vs/platform/label/common/label'; export class BrowserMain extends Disposable { @@ -134,6 +137,8 @@ export class BrowserMain extends Disposable { const progessService = accessor.get(IProgressService); const environmentService = accessor.get(IBrowserWorkbenchEnvironmentService); const instantiationService = accessor.get(IInstantiationService); + const remoteExplorerService = accessor.get(IRemoteExplorerService); + const labelService = accessor.get(ILabelService); const embedderLogger = instantiationService.createInstance(DelayedLogChannel, 'webEmbedder', productService.embedderIdentifier || localize('vscode.dev', "vscode.dev"), joinPath(dirname(environmentService.logFile), `webEmbedder.log`)); @@ -163,7 +168,26 @@ export class BrowserMain extends Disposable { window: { withProgress: (options, task) => progessService.withProgress(options, task) }, - shutdown: () => lifecycleService.shutdown() + shutdown: () => lifecycleService.shutdown(), + openTunnel: async (tunnelOptions) => { + const tunnel = await remoteExplorerService.forward({ + remote: tunnelOptions.remoteAddress, + local: tunnelOptions.localAddressPort, + name: tunnelOptions.label, + source: { + source: TunnelSource.Extension, + description: labelService.getHostLabel(Schemas.vscodeRemote, this.configuration.remoteAuthority) + }, + elevateIfNeeded: false + }); + if (!tunnel) { + throw new Error('cannot open tunnel'); + } + + return new class extends DisposableTunnel implements ITunnel { + override localAddress!: string; + }({ port: tunnel.tunnelRemotePort, host: tunnel.tunnelRemoteHost }, tunnel.localAddress, () => tunnel.dispose()); + } }; }); } From 5a43663aed2c063b4bb0d7512f9df0d77668b9ca Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Thu, 16 Jun 2022 17:15:49 +0200 Subject: [PATCH 096/175] Avoid downloading builtin extensions (#152339) --- .github/workflows/basic.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/basic.yml b/.github/workflows/basic.yml index 025455a1afb..b5fe086de05 100644 --- a/.github/workflows/basic.yml +++ b/.github/workflows/basic.yml @@ -62,7 +62,7 @@ jobs: run: yarn --frozen-lockfile --network-timeout 180000 - name: Compile and Download - run: yarn npm-run-all --max_old_space_size=4095 -lp compile "electron x64" download-builtin-extensions + run: yarn npm-run-all --max_old_space_size=4095 -lp compile "electron x64" - name: Run Unit Tests id: electron-unit-tests From 427834d3c2399cbd39a230d041337f31d20306ea Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 16 Jun 2022 08:31:44 -0700 Subject: [PATCH 097/175] xterm-addon-webgl@0.12.0-beta.39 This is one of those odd cases where the latest (.40) is actually older than the one before because the merged happened close by. This brings in the light-height webgl powerline fix xtermjs/xterm.js#3862 --- package.json | 2 +- remote/package.json | 2 +- remote/web/package.json | 2 +- remote/web/yarn.lock | 8 ++++---- remote/yarn.lock | 8 ++++---- yarn.lock | 8 ++++---- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 6db4d010819..2111f09eb02 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,7 @@ "xterm-addon-search": "0.9.0-beta.39", "xterm-addon-serialize": "0.7.0-beta.12", "xterm-addon-unicode11": "0.4.0-beta.3", - "xterm-addon-webgl": "0.12.0-beta.38", + "xterm-addon-webgl": "0.12.0-beta.39", "xterm-headless": "4.19.0-beta.60", "yauzl": "^2.9.2", "yazl": "^2.4.3" diff --git a/remote/package.json b/remote/package.json index c42700d6e3c..0544947abb0 100644 --- a/remote/package.json +++ b/remote/package.json @@ -30,7 +30,7 @@ "xterm-addon-search": "0.9.0-beta.39", "xterm-addon-serialize": "0.7.0-beta.12", "xterm-addon-unicode11": "0.4.0-beta.3", - "xterm-addon-webgl": "0.12.0-beta.38", + "xterm-addon-webgl": "0.12.0-beta.39", "xterm-headless": "4.19.0-beta.60", "yauzl": "^2.9.2", "yazl": "^2.4.3" diff --git a/remote/web/package.json b/remote/web/package.json index 85329a33d0b..bb4ed6ee77d 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -15,6 +15,6 @@ "xterm": "4.19.0-beta.60", "xterm-addon-search": "0.9.0-beta.39", "xterm-addon-unicode11": "0.4.0-beta.3", - "xterm-addon-webgl": "0.12.0-beta.38" + "xterm-addon-webgl": "0.12.0-beta.39" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 7bd6d53622e..b837505240e 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -141,10 +141,10 @@ xterm-addon-unicode11@0.4.0-beta.3: resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.4.0-beta.3.tgz#f350184155fafd5ad0d6fbf31d13e6ca7dea1efa" integrity sha512-FryZAVwbUjKTmwXnm1trch/2XO60F5JsDvOkZhzobV1hm10sFLVuZpFyHXiUx7TFeeFsvNP+S77LAtWoeT5z+Q== -xterm-addon-webgl@0.12.0-beta.38: - version "0.12.0-beta.38" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.38.tgz#59fc8f3711e38b9a238fd460cd070007b7904e7a" - integrity sha512-fqRhfwUx/WZlDLFsZX26irLYTdRGMf1xuRwoqfr4KiFoQvHye55U8VJ4WLHdWglN2Kzh8PbINk5HfMhIwYHe9Q== +xterm-addon-webgl@0.12.0-beta.39: + version "0.12.0-beta.39" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.39.tgz#f5fafd2c4d1ec046ff6b57edf850234948cf630a" + integrity sha512-116gk9KYqbMyCcjedDYhqg3RKl0CI19I1l/+u8JdJb6bucv835Hj60W2it5AAsmvw726FEXwhMoOaY3gs+n1/g== xterm@4.19.0-beta.60: version "4.19.0-beta.60" diff --git a/remote/yarn.lock b/remote/yarn.lock index c56db3d8e0c..b216f82db48 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -947,10 +947,10 @@ xterm-addon-unicode11@0.4.0-beta.3: resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.4.0-beta.3.tgz#f350184155fafd5ad0d6fbf31d13e6ca7dea1efa" integrity sha512-FryZAVwbUjKTmwXnm1trch/2XO60F5JsDvOkZhzobV1hm10sFLVuZpFyHXiUx7TFeeFsvNP+S77LAtWoeT5z+Q== -xterm-addon-webgl@0.12.0-beta.38: - version "0.12.0-beta.38" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.38.tgz#59fc8f3711e38b9a238fd460cd070007b7904e7a" - integrity sha512-fqRhfwUx/WZlDLFsZX26irLYTdRGMf1xuRwoqfr4KiFoQvHye55U8VJ4WLHdWglN2Kzh8PbINk5HfMhIwYHe9Q== +xterm-addon-webgl@0.12.0-beta.39: + version "0.12.0-beta.39" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.39.tgz#f5fafd2c4d1ec046ff6b57edf850234948cf630a" + integrity sha512-116gk9KYqbMyCcjedDYhqg3RKl0CI19I1l/+u8JdJb6bucv835Hj60W2it5AAsmvw726FEXwhMoOaY3gs+n1/g== xterm-headless@4.19.0-beta.60: version "4.19.0-beta.60" diff --git a/yarn.lock b/yarn.lock index 31a0f3d05df..ab45aaaeb4b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12271,10 +12271,10 @@ xterm-addon-unicode11@0.4.0-beta.3: resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.4.0-beta.3.tgz#f350184155fafd5ad0d6fbf31d13e6ca7dea1efa" integrity sha512-FryZAVwbUjKTmwXnm1trch/2XO60F5JsDvOkZhzobV1hm10sFLVuZpFyHXiUx7TFeeFsvNP+S77LAtWoeT5z+Q== -xterm-addon-webgl@0.12.0-beta.38: - version "0.12.0-beta.38" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.38.tgz#59fc8f3711e38b9a238fd460cd070007b7904e7a" - integrity sha512-fqRhfwUx/WZlDLFsZX26irLYTdRGMf1xuRwoqfr4KiFoQvHye55U8VJ4WLHdWglN2Kzh8PbINk5HfMhIwYHe9Q== +xterm-addon-webgl@0.12.0-beta.39: + version "0.12.0-beta.39" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.39.tgz#f5fafd2c4d1ec046ff6b57edf850234948cf630a" + integrity sha512-116gk9KYqbMyCcjedDYhqg3RKl0CI19I1l/+u8JdJb6bucv835Hj60W2it5AAsmvw726FEXwhMoOaY3gs+n1/g== xterm-headless@4.19.0-beta.60: version "4.19.0-beta.60" From 05a2f13f84e4da748b456cd861ba54c3fa6f549b Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 16 Jun 2022 18:34:14 +0200 Subject: [PATCH 098/175] Refactors 3wm code. --- .../mergeEditor/browser/diffComputer.ts | 92 ++- .../mergeEditor/browser/mergeEditor.ts | 26 +- .../mergeEditor/browser/mergeEditorModel.ts | 14 +- .../contrib/mergeEditor/browser/model.ts | 619 ++++++++---------- .../mergeEditor/browser/textModelDiffs.ts | 24 +- 5 files changed, 386 insertions(+), 389 deletions(-) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/diffComputer.ts b/src/vs/workbench/contrib/mergeEditor/browser/diffComputer.ts index e468085027c..9e3d8083a40 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/diffComputer.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/diffComputer.ts @@ -3,18 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { BugIndicatingError } from 'vs/base/common/errors'; import { Range } from 'vs/editor/common/core/range'; import { ICharChange, IDiffComputationResult, ILineChange } from 'vs/editor/common/diff/diffComputer'; import { ITextModel } from 'vs/editor/common/model'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; -import { LineRange, LineRangeMapping, RangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model'; +import { LineRange, DetailedLineRangeMapping, RangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model'; export interface IDiffComputer { computeDiff(textModel1: ITextModel, textModel2: ITextModel): Promise; } export interface IDiffComputerResult { - diffs: LineRangeMapping[] | null; + diffs: DetailedLineRangeMapping[] | null; } export class EditorWorkerServiceDiffComputer implements IDiffComputer { @@ -28,12 +29,12 @@ export class EditorWorkerServiceDiffComputer implements IDiffComputer { return { diffs: EditorWorkerServiceDiffComputer.fromDiffComputationResult(diffs, textModel1, textModel2) }; } - public static fromDiffComputationResult(result: IDiffComputationResult, textModel1: ITextModel, textModel2: ITextModel): LineRangeMapping[] { + public static fromDiffComputationResult(result: IDiffComputationResult, textModel1: ITextModel, textModel2: ITextModel): DetailedLineRangeMapping[] { return result.changes.map((c) => fromLineChange(c, textModel1, textModel2)); } } -function fromLineChange(lineChange: ILineChange, originalTextModel: ITextModel, modifiedTextModel: ITextModel): LineRangeMapping { +function fromLineChange(lineChange: ILineChange, originalTextModel: ITextModel, modifiedTextModel: ITextModel): DetailedLineRangeMapping { let originalRange: LineRange; if (lineChange.originalEndLineNumber === 0) { // Insertion @@ -50,16 +51,16 @@ function fromLineChange(lineChange: ILineChange, originalTextModel: ITextModel, modifiedRange = new LineRange(lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1); } - let innerDiffs = lineChange.charChanges?.map(c => rangeMappingFromCharChange(c)); + let innerDiffs = lineChange.charChanges?.map(c => rangeMappingFromCharChange(c, originalTextModel, modifiedTextModel)); if (!innerDiffs) { innerDiffs = [rangeMappingFromLineRanges(originalRange, modifiedRange)]; } - return new LineRangeMapping( - originalTextModel, + return new DetailedLineRangeMapping( originalRange, - modifiedTextModel, + originalTextModel, modifiedRange, + modifiedTextModel, innerDiffs ); } @@ -81,9 +82,80 @@ function rangeMappingFromLineRanges(originalRange: LineRange, modifiedRange: Lin ); } -function rangeMappingFromCharChange(charChange: ICharChange): RangeMapping { - return new RangeMapping( +function rangeMappingFromCharChange(charChange: ICharChange, inputTextModel: ITextModel, modifiedTextModel: ITextModel): RangeMapping { + return normalizeRangeMapping(new RangeMapping( new Range(charChange.originalStartLineNumber, charChange.originalStartColumn, charChange.originalEndLineNumber, charChange.originalEndColumn), new Range(charChange.modifiedStartLineNumber, charChange.modifiedStartColumn, charChange.modifiedEndLineNumber, charChange.modifiedEndColumn) + ), inputTextModel, modifiedTextModel); +} + +function normalizeRangeMapping(rangeMapping: RangeMapping, inputTextModel: ITextModel, outputTextModel: ITextModel): RangeMapping { + const inputRangeEmpty = rangeMapping.inputRange.isEmpty(); + const outputRangeEmpty = rangeMapping.outputRange.isEmpty(); + + if (inputRangeEmpty && outputRangeEmpty) { + throw new BugIndicatingError(); // This case makes no sense, but it is an edge case we need to rule out + } + + const originalStartsAtEndOfLine = isAtEndOfLine(rangeMapping.inputRange.startLineNumber, rangeMapping.inputRange.startColumn, inputTextModel); + const modifiedStartsAtEndOfLine = isAtEndOfLine(rangeMapping.outputRange.startLineNumber, rangeMapping.outputRange.startColumn, outputTextModel); + + if (!inputRangeEmpty && !outputRangeEmpty && originalStartsAtEndOfLine && modifiedStartsAtEndOfLine) { + // a b c [\n] x y z \n + // d e f [\n a] \n + // -> + // a b c \n [] x y z \n + // d e f \n [a] \n + + return new RangeMapping( + rangeMapping.inputRange.setStartPosition(rangeMapping.inputRange.startLineNumber + 1, 1), + + rangeMapping.outputRange.setStartPosition(rangeMapping.outputRange.startLineNumber + 1, 1), + ); + } + + if ( + modifiedStartsAtEndOfLine && + originalStartsAtEndOfLine && + ((inputRangeEmpty && rangeEndsAtEndOfLine(rangeMapping.outputRange, outputTextModel)) || + (outputRangeEmpty && rangeEndsAtEndOfLine(rangeMapping.inputRange, inputTextModel))) + ) { + // o: a b c [] \n x y z \n + // m: d e f [\n a] \n + // -> + // o: a b c \n [] x y z \n + // m: d e f \n [a \n] + + // or + + // a b c [\n x y z] \n + // d e f [] \n a \n + // -> + // a b c \n [x y z \n] + // d e f \n [] a \n + + return new RangeMapping( + moveRange(rangeMapping.inputRange), + moveRange(rangeMapping.outputRange) + ); + } + + return rangeMapping; +} + +function isAtEndOfLine(lineNumber: number, column: number, model: ITextModel): boolean { + return column >= model.getLineMaxColumn(lineNumber); +} + +function rangeEndsAtEndOfLine(range: Range, model: ITextModel,): boolean { + return isAtEndOfLine(range.endLineNumber, range.endColumn, model); +} + +function moveRange(range: Range): Range { + return new Range( + range.startLineNumber + 1, + 1, + range.endLineNumber + 1, + 1, ); } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts index ae4b7fbe490..9c89ef79a0e 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts @@ -47,7 +47,7 @@ import { applyTextEditorOptions } from 'vs/workbench/common/editor/editorOptions import { autorun, autorunWithStore, derivedObservable, IObservable, ITransaction, keepAlive, ObservableValue, transaction } from 'vs/workbench/contrib/audioCues/browser/observable'; import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; import { MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorModel'; -import { DocumentMapping, LineRange, SimpleLineRangeMapping, ToggleState } from 'vs/workbench/contrib/mergeEditor/browser/model'; +import { DocumentMapping, LineRange, LineRangeMapping, InputState } from 'vs/workbench/contrib/mergeEditor/browser/model'; import { applyObservableDecorations, join, n, ReentrancyBarrier, setStyle } from 'vs/workbench/contrib/mergeEditor/browser/utils'; import { settingsSashBorder } from 'vs/workbench/contrib/preferences/common/settingsEditorColorRegistry'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -109,12 +109,12 @@ export class MergeEditor extends AbstractTextEditor { return undefined; } const resultDiffs = model.resultDiffs.read(reader); - const modifiedBaseRanges = DocumentMapping.fromDiffs(model.input1LinesDiffs.read(reader), resultDiffs, model.input1.getLineCount()); + const modifiedBaseRanges = DocumentMapping.betweenOutputs(model.input1LinesDiffs.read(reader), resultDiffs, model.input1.getLineCount()); return new DocumentMapping( modifiedBaseRanges.lineRangeMappings.map((m) => m.inputRange.isEmpty || m.outputRange.isEmpty - ? new SimpleLineRangeMapping( + ? new LineRangeMapping( m.inputRange.deltaStart(-1), m.outputRange.deltaStart(-1) ) @@ -129,12 +129,12 @@ export class MergeEditor extends AbstractTextEditor { return undefined; } const resultDiffs = model.resultDiffs.read(reader); - const modifiedBaseRanges = DocumentMapping.fromDiffs(model.input2LinesDiffs.read(reader), resultDiffs, model.input2.getLineCount()); + const modifiedBaseRanges = DocumentMapping.betweenOutputs(model.input2LinesDiffs.read(reader), resultDiffs, model.input2.getLineCount()); return new DocumentMapping( modifiedBaseRanges.lineRangeMappings.map((m) => m.inputRange.isEmpty || m.outputRange.isEmpty - ? new SimpleLineRangeMapping( + ? new LineRangeMapping( m.inputRange.deltaStart(-1), m.outputRange.deltaStart(-1) ) @@ -559,8 +559,8 @@ class InputCodeEditorView extends CodeEditorView { const inputDiffs = m.getInputDiffs(this.inputNumber); for (const diff of inputDiffs) { - if (diff.innerRangeMappings) { - for (const d of diff.innerRangeMappings) { + if (diff.rangeMappings) { + for (const d of diff.rangeMappings) { result.push({ range: d.outputRange, options: { @@ -624,7 +624,7 @@ class InputCodeEditorView extends CodeEditorView { interface ModifiedBaseRangeGutterItemInfo extends IGutterItemInfo { enabled: IObservable; - toggleState: IObservable; + toggleState: IObservable; setState(value: boolean, tx: ITransaction): void; } @@ -645,11 +645,11 @@ class MergeConflictGutterItemView extends Disposable implements IGutterItemView< autorun((reader) => { const item = this.item.read(reader)!; const value = item.toggleState.read(reader); - const iconMap: Record = { - [ToggleState.unset]: { icon: undefined, checked: false }, - [ToggleState.conflicting]: { icon: Codicon.circleFilled, checked: false }, - [ToggleState.first]: { icon: Codicon.check, checked: true }, - [ToggleState.second]: { icon: Codicon.checkAll, checked: true }, + const iconMap: Record = { + [InputState.excluded]: { icon: undefined, checked: false }, + [InputState.conflicting]: { icon: Codicon.circleFilled, checked: false }, + [InputState.first]: { icon: Codicon.check, checked: true }, + [InputState.second]: { icon: Codicon.checkAll, checked: true }, }; checkBox.setIcon(iconMap[value].icon); checkBox.checked = iconMap[value].checked; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts index 16cd2dbb5c0..135d6a2d64a 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts @@ -14,7 +14,7 @@ import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { EditorModel } from 'vs/workbench/common/editor/editorModel'; import { autorunHandleChanges, derivedObservable, derivedObservableWithCache, IObservable, ITransaction, keepAlive, ObservableValue, transaction, waitForState } from 'vs/workbench/contrib/audioCues/browser/observable'; import { EditorWorkerServiceDiffComputer } from 'vs/workbench/contrib/mergeEditor/browser/diffComputer'; -import { LineRangeMapping, LineRangeEdit, LineRange, ModifiedBaseRange, ModifiedBaseRangeState, RangeEdit } from 'vs/workbench/contrib/mergeEditor/browser/model'; +import { LineRangeEdit, LineRange, ModifiedBaseRange, ModifiedBaseRangeState, RangeEdit, DetailedLineRangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model'; import { TextModelDiffChangeReason, TextModelDiffs, TextModelDiffState } from 'vs/workbench/contrib/mergeEditor/browser/textModelDiffs'; import { concatArrays, leftJoin, elementAtOrUndefined } from 'vs/workbench/contrib/mergeEditor/browser/utils'; @@ -58,7 +58,7 @@ export class MergeEditorModel extends EditorModel { const input1Diffs = this.input1TextModelDiffs.diffs.read(reader); const input2Diffs = this.input2TextModelDiffs.diffs.read(reader); - return ModifiedBaseRange.fromDiffs(this.base, this.input1, input1Diffs, this.input2, input2Diffs); + return ModifiedBaseRange.fromDiffs(input1Diffs, input2Diffs, this.base, this.input1, this.input2); }); public readonly input1LinesDiffs = this.input1TextModelDiffs.diffs; @@ -114,7 +114,7 @@ export class MergeEditorModel extends EditorModel { }); } - private recomputeState(resultDiffs: LineRangeMapping[], stores: Map>): void { + private recomputeState(resultDiffs: DetailedLineRangeMapping[], stores: Map>): void { transaction(tx => { const baseRangeWithStoreAndTouchingDiffs = leftJoin( stores, @@ -199,13 +199,13 @@ export class MergeEditorModel extends EditorModel { } } - private computeState(baseRange: ModifiedBaseRange, conflictingDiffs: LineRangeMapping[]): ModifiedBaseRangeState { + private computeState(baseRange: ModifiedBaseRange, conflictingDiffs: DetailedLineRangeMapping[]): ModifiedBaseRangeState { if (conflictingDiffs.length === 0) { return ModifiedBaseRangeState.default; } const conflictingEdits = conflictingDiffs.map((d) => d.getLineEdit()); - function editsAgreeWithDiffs(diffs: readonly LineRangeMapping[]): boolean { + function editsAgreeWithDiffs(diffs: readonly DetailedLineRangeMapping[]): boolean { return equals( conflictingEdits, diffs.map((d) => d.getLineEdit()), @@ -279,10 +279,10 @@ function getEditForBase(baseRange: ModifiedBaseRange, state: ModifiedBaseRangeSt function combineInputs(baseRange: ModifiedBaseRange, firstInput: 1 | 2): LineRangeEdit | undefined { const combinedDiffs = concatArrays( baseRange.input1Diffs.flatMap((diffs) => - diffs.innerRangeMappings.map((diff) => ({ diff, input: 1 as const })) + diffs.rangeMappings.map((diff) => ({ diff, input: 1 as const })) ), baseRange.input2Diffs.flatMap((diffs) => - diffs.innerRangeMappings.map((diff) => ({ diff, input: 2 as const })) + diffs.rangeMappings.map((diff) => ({ diff, input: 2 as const })) ) ).sort( tieBreakComparators( diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model.ts b/src/vs/workbench/contrib/mergeEditor/browser/model.ts index 49d94a340dc..5880c4a5275 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model.ts @@ -7,67 +7,7 @@ import { Comparator, compareBy, equals, findLast, numberComparator } from 'vs/ba import { BugIndicatingError } from 'vs/base/common/errors'; import { Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; - -/** - * Represents an edit, expressed in whole lines: - * At {@link LineRange.startLineNumber}, delete {@link LineRange.lineCount} many lines and insert {@link newLines}. -*/ -export class LineRangeEdit { - constructor( - public readonly range: LineRange, - public readonly newLines: string[] - ) { } - - public equals(other: LineRangeEdit): boolean { - return this.range.equals(other.range) && equals(this.newLines, other.newLines); - } - - public apply(model: ITextModel): void { - new LineEdits([this]).apply(model); - } -} - -export class RangeEdit { - constructor( - public readonly range: Range, - public readonly newText: string - ) { } - - public equals(other: RangeEdit): boolean { - return Range.equalsRange(this.range, other.range) && this.newText === other.newText; - } -} - -export class LineEdits { - constructor(public readonly edits: readonly LineRangeEdit[]) { } - - public apply(model: ITextModel): void { - model.pushEditOperations( - null, - this.edits.map((e) => { - if (e.range.endLineNumberExclusive <= model.getLineCount()) { - return { - range: new Range(e.range.startLineNumber, 1, e.range.endLineNumberExclusive, 1), - text: e.newLines.map(s => s + '\n').join(''), - }; - } - - if (e.range.startLineNumber === 1) { - return { - range: new Range(1, 1, model.getLineCount(), Number.MAX_SAFE_INTEGER), - text: e.newLines.join('\n'), - }; - } - - return { - range: new Range(e.range.startLineNumber - 1, Number.MAX_SAFE_INTEGER, model.getLineCount(), Number.MAX_SAFE_INTEGER), - text: e.newLines.map(s => '\n' + s).join(''), - }; - }), - () => null - ); - } -} +import { concatArrays } from 'vs/workbench/contrib/mergeEditor/browser/utils'; export class LineRange { public static readonly compareByStart: Comparator = compareBy(l => l.startLineNumber, numberComparator); @@ -167,63 +107,35 @@ export class LineRange { } export class LineRangeMapping { - public static hull(lineDiffs: readonly LineRangeMapping[]): LineRangeMapping | undefined { - if (lineDiffs.length === 0) { - return undefined; - } - - return new LineRangeMapping( - lineDiffs[0].inputTextModel, - LineRange.join(lineDiffs.map((d) => d.inputRange))!, - lineDiffs[0].outputTextModel, - LineRange.join(lineDiffs.map((d) => d.outputRange))!, - [] - ); + public static join(mappings: readonly LineRangeMapping[]): LineRangeMapping | undefined { + return mappings.reduce((acc, cur) => acc ? acc.join(cur) : cur, undefined); } - public static alignOriginalRange(lineDiffs: readonly LineRangeMapping[]): LineRangeMapping[] { - if (lineDiffs.length === 0) { - return []; - } - const originalRange = LineRange.join(lineDiffs.map((d) => d.inputRange))!; - return lineDiffs.map(l => l.extendInputRange(originalRange)); - } - - public readonly innerRangeMappings: readonly RangeMapping[]; - constructor( - public readonly inputTextModel: ITextModel, public readonly inputRange: LineRange, - public readonly outputTextModel: ITextModel, - public readonly outputRange: LineRange, - innerRangeMappings?: readonly RangeMapping[], - ) { - this.innerRangeMappings = innerRangeMappings - ? innerRangeMappings - : [ - new RangeMapping( - this.inputRange.toRange(), - this.outputRange.toRange() - ), - ]; - } + public readonly outputRange: LineRange + ) { } - public extendInputRange(extendedOriginalRange: LineRange): LineRangeMapping { - if (!extendedOriginalRange.containsRange(this.inputRange)) { + public extendInputRange(extendedInputRange: LineRange): LineRangeMapping { + if (!extendedInputRange.containsRange(this.inputRange)) { throw new BugIndicatingError(); } - const startDelta = extendedOriginalRange.startLineNumber - this.inputRange.startLineNumber; - const endDelta = extendedOriginalRange.endLineNumberExclusive - this.inputRange.endLineNumberExclusive; + const startDelta = extendedInputRange.startLineNumber - this.inputRange.startLineNumber; + const endDelta = extendedInputRange.endLineNumberExclusive - this.inputRange.endLineNumberExclusive; return new LineRangeMapping( - this.inputTextModel, - extendedOriginalRange, - this.outputTextModel, + extendedInputRange, new LineRange( this.outputRange.startLineNumber + startDelta, this.outputRange.lineCount - startDelta + endDelta - ), - this.innerRangeMappings, + ) + ); + } + + public join(other: LineRangeMapping): LineRangeMapping { + return new LineRangeMapping( + this.inputRange.join(other.inputRange), + this.outputRange.join(other.outputRange) ); } @@ -231,30 +143,233 @@ export class LineRangeMapping { return this.outputRange.endLineNumberExclusive - this.inputRange.endLineNumberExclusive; } - private ensureSameInputModel(other: LineRangeMapping): void { - if (this.inputTextModel !== other.inputTextModel) { - // Both changes must refer to the same original model - throw new BugIndicatingError(); - } + public toString(): string { + return `${this.inputRange.toString()} -> ${this.outputRange.toString()}`; } - public isStrictBefore(other: LineRangeMapping): boolean { - this.ensureSameInputModel(other); - return this.inputRange.endLineNumberExclusive <= other.inputRange.startLineNumber; + public addOutputLineDelta(delta: number): LineRangeMapping { + return new LineRangeMapping( + this.inputRange, + this.outputRange.delta(delta) + ); + } +} + +export class MappingAlignment { + public static compute( + fromBaseToInput1: readonly T[], + fromBaseToInput2: readonly T[] + ): MappingAlignment[] { + const compareByStartLineNumber = compareBy( + (d) => d.inputRange.startLineNumber, + numberComparator + ); + + const combinedDiffs = concatArrays( + fromBaseToInput1.map((diff) => ({ source: 0 as const, diff })), + fromBaseToInput2.map((diff) => ({ source: 1 as const, diff })) + ).sort(compareBy((d) => d.diff, compareByStartLineNumber)); + + const currentDiffs = [new Array(), new Array()]; + const deltaFromBaseToInput = [0, 0]; + + const alignments = new Array>(); + + function pushAndReset(baseRange: LineRange) { + const mapping1 = LineRangeMapping.join(currentDiffs[0]) || new LineRangeMapping(baseRange, baseRange.delta(deltaFromBaseToInput[0])); + const mapping2 = LineRangeMapping.join(currentDiffs[1]) || new LineRangeMapping(baseRange, baseRange.delta(deltaFromBaseToInput[1])); + + alignments.push( + new MappingAlignment( + currentInputRange!, + mapping1.extendInputRange(currentInputRange!).outputRange, + currentDiffs[0], + mapping2.extendInputRange(currentInputRange!).outputRange, + currentDiffs[1] + ) + ); + currentDiffs[0] = []; + currentDiffs[1] = []; + } + + let currentInputRange: LineRange | undefined; + + for (const diff of combinedDiffs) { + const range = diff.diff.inputRange; + if (currentInputRange && !currentInputRange.touches(range)) { + pushAndReset(currentInputRange); + currentInputRange = undefined; + } + deltaFromBaseToInput[diff.source] = + diff.diff.resultingDeltaFromOriginalToModified; + currentInputRange = currentInputRange ? currentInputRange.join(range) : range; + currentDiffs[diff.source].push(diff.diff); + } + if (currentInputRange) { + pushAndReset(currentInputRange); + } + + return alignments; + } + + constructor( + public readonly baseRange: LineRange, + public readonly input1Range: LineRange, + public readonly input1LineMappings: T[], + public readonly input2Range: LineRange, + public readonly input2LineMappings: T[], + ) { + } + + toString(): string { + return `${this.input1Range} <- ${this.baseRange} -> ${this.input2Range}`; + } +} + +export class DocumentMapping { + public static betweenOutputs( + inputToOutput1: readonly LineRangeMapping[], + inputToOutput2: readonly LineRangeMapping[], + inputLineCount: number + ): DocumentMapping { + const alignments = MappingAlignment.compute(inputToOutput1, inputToOutput2); + const mappings = alignments.map((m) => new LineRangeMapping(m.input1Range, m.input2Range)); + return new DocumentMapping(mappings, inputLineCount); + } + + public getOutputLine(inputLineNumber: number): number | LineRangeMapping { + const lastBefore = findLast(this.lineRangeMappings, r => r.inputRange.startLineNumber <= inputLineNumber); + if (lastBefore) { + if (lastBefore.inputRange.contains(inputLineNumber)) { + return lastBefore; + } + return inputLineNumber + lastBefore.outputRange.endLineNumberExclusive - lastBefore.inputRange.endLineNumberExclusive; + } + return inputLineNumber; + } + + public getInputLine(outputLineNumber: number): number | LineRangeMapping { + const lastBefore = findLast(this.lineRangeMappings, r => r.outputRange.startLineNumber <= outputLineNumber); + if (lastBefore) { + if (lastBefore.outputRange.contains(outputLineNumber)) { + return lastBefore; + } + return outputLineNumber + lastBefore.inputRange.endLineNumberExclusive - lastBefore.outputRange.endLineNumberExclusive; + } + return outputLineNumber; + } + + constructor( + public readonly lineRangeMappings: LineRangeMapping[], + public readonly inputLineCount: number + ) { } +} + +/** + * Represents an edit, expressed in whole lines: + * At (before) {@link LineRange.startLineNumber}, delete {@link LineRange.lineCount} many lines and insert {@link newLines}. +*/ +export class LineRangeEdit { + constructor( + public readonly range: LineRange, + public readonly newLines: string[] + ) { } + + public equals(other: LineRangeEdit): boolean { + return this.range.equals(other.range) && equals(this.newLines, other.newLines); + } + + public apply(model: ITextModel): void { + new LineEdits([this]).apply(model); + } +} + +export class RangeEdit { + constructor( + public readonly range: Range, + public readonly newText: string + ) { } + + public equals(other: RangeEdit): boolean { + return Range.equalsRange(this.range, other.range) && this.newText === other.newText; + } +} + +export class LineEdits { + constructor(public readonly edits: readonly LineRangeEdit[]) { } + + public apply(model: ITextModel): void { + model.pushEditOperations( + null, + this.edits.map((e) => { + if (e.range.endLineNumberExclusive <= model.getLineCount()) { + return { + range: new Range(e.range.startLineNumber, 1, e.range.endLineNumberExclusive, 1), + text: e.newLines.map(s => s + '\n').join(''), + }; + } + + if (e.range.startLineNumber === 1) { + return { + range: new Range(1, 1, model.getLineCount(), Number.MAX_SAFE_INTEGER), + text: e.newLines.join('\n'), + }; + } + + return { + range: new Range(e.range.startLineNumber - 1, Number.MAX_SAFE_INTEGER, model.getLineCount(), Number.MAX_SAFE_INTEGER), + text: e.newLines.map(s => '\n' + s).join(''), + }; + }), + () => null + ); + } +} + +export class DetailedLineRangeMapping extends LineRangeMapping { + public static override join(mappings: readonly DetailedLineRangeMapping[]): DetailedLineRangeMapping | undefined { + return mappings.reduce((acc, cur) => acc ? acc.join(cur) : cur, undefined); + } + + public readonly rangeMappings: readonly RangeMapping[]; + + constructor( + inputRange: LineRange, + public readonly inputTextModel: ITextModel, + outputRange: LineRange, + public readonly outputTextModel: ITextModel, + rangeMappings?: readonly RangeMapping[], + ) { + super(inputRange, outputRange); + + this.rangeMappings = rangeMappings || [new RangeMapping(this.inputRange.toRange(), this.outputRange.toRange())]; + } + + public override addOutputLineDelta(delta: number): DetailedLineRangeMapping { + return new DetailedLineRangeMapping( + this.inputRange, + this.inputTextModel, + this.outputRange.delta(delta), + this.outputTextModel, + this.rangeMappings.map(d => d.addOutputLineDelta(delta)) + ); + } + + public override join(other: DetailedLineRangeMapping): DetailedLineRangeMapping { + return new DetailedLineRangeMapping( + this.inputRange.join(other.inputRange), + this.inputTextModel, + this.outputRange.join(other.outputRange), + this.outputTextModel, + ); } public getLineEdit(): LineRangeEdit { - return new LineRangeEdit( - this.inputRange, - this.getOutputLines() - ); + return new LineRangeEdit(this.inputRange, this.getOutputLines()); } public getReverseLineEdit(): LineRangeEdit { - return new LineRangeEdit( - this.outputRange, - this.getInputLines() - ); + return new LineRangeEdit(this.outputRange, this.getInputLines()); } private getOutputLines(): string[] { @@ -264,16 +379,6 @@ export class LineRangeMapping { private getInputLines(): string[] { return this.inputRange.getLines(this.inputTextModel); } - - public addOutputLineDelta(delta: number): LineRangeMapping { - return new LineRangeMapping( - this.inputTextModel, - this.inputRange, - this.outputTextModel, - this.outputRange.delta(delta), - this.innerRangeMappings.map(d => d.addOutputLineDelta(delta)) - ); - } } export class RangeMapping { @@ -281,7 +386,12 @@ export class RangeMapping { } toString(): string { - return `${this.inputRange.toString()} -> ${this.outputRange.toString()}`; + function rangeToString(range: Range) { + // TODO@hediet make this the default Range.toString + return `[${range.startLineNumber}:${range.startColumn}, ${range.endLineNumber}:${range.endColumn})`; + } + + return `${rangeToString(this.inputRange)} -> ${rangeToString(this.outputRange)}`; } addOutputLineDelta(deltaLines: number): RangeMapping { @@ -310,116 +420,52 @@ export class ModifiedBaseRange { * This method computes strongly connected components of that graph while maintaining the side of each diff. */ public static fromDiffs( + diffs1: readonly DetailedLineRangeMapping[], + diffs2: readonly DetailedLineRangeMapping[], baseTextModel: ITextModel, input1TextModel: ITextModel, - diffs1: readonly LineRangeMapping[], input2TextModel: ITextModel, - diffs2: readonly LineRangeMapping[] ): ModifiedBaseRange[] { - const compareByStartLineNumber = compareBy( - (d) => d.inputRange.startLineNumber, - numberComparator + const alignments = MappingAlignment.compute(diffs1, diffs2); + return alignments.map( + (a) => + new ModifiedBaseRange( + a.baseRange, + baseTextModel, + a.input1Range, + input1TextModel, + a.input1LineMappings, + a.input2Range, + input2TextModel, + a.input2LineMappings + ) ); - - const diffs = diffs1 - .map((diff) => ({ source: 0 as 0 | 1, diff })) - .concat(diffs2.map((diff) => ({ source: 1 as const, diff }))); - - diffs.sort(compareBy(d => d.diff, compareByStartLineNumber)); - - const currentDiffs = [ - new Array(), - new Array(), - ]; - const deltaFromBaseToInput = [0, 0]; - - const result = new Array(); - - function pushAndReset() { - if (currentDiffs[0].length === 0 && currentDiffs[1].length === 0) { - return; - } - result.push(new ModifiedBaseRange( - baseTextModel, - input1TextModel, - currentDiffs[0], - deltaFromBaseToInput[0], - input2TextModel, - currentDiffs[1], - deltaFromBaseToInput[1], - )); - currentDiffs[0] = []; - currentDiffs[1] = []; - } - - let currentRange: LineRange | undefined; - - for (const diff of diffs) { - const range = diff.diff.inputRange; - if (currentRange && !currentRange.touches(range)) { - pushAndReset(); - currentRange = undefined; - } - deltaFromBaseToInput[diff.source] = diff.diff.resultingDeltaFromOriginalToModified; - currentRange = currentRange ? currentRange.join(range) : range; - currentDiffs[diff.source].push(diff.diff); - } - pushAndReset(); - - return result; } - public readonly input1CombinedDiff = LineRangeMapping.hull(this.input1Diffs); - public readonly input2CombinedDiff = LineRangeMapping.hull(this.input2Diffs); + public readonly input1CombinedDiff = DetailedLineRangeMapping.join(this.input1Diffs); + public readonly input2CombinedDiff = DetailedLineRangeMapping.join(this.input2Diffs); - public readonly baseRange: LineRange; - public readonly input1Range: LineRange; - public readonly input2Range: LineRange; constructor( + public readonly baseRange: LineRange, public readonly baseTextModel: ITextModel, + public readonly input1Range: LineRange, public readonly input1TextModel: ITextModel, - public readonly input1Diffs: readonly LineRangeMapping[], - input1DeltaLineCount: number, + public readonly input1Diffs: readonly DetailedLineRangeMapping[], + public readonly input2Range: LineRange, public readonly input2TextModel: ITextModel, - public readonly input2Diffs: readonly LineRangeMapping[], - input2DeltaLineCount: number, + public readonly input2Diffs: readonly DetailedLineRangeMapping[], ) { if (this.input1Diffs.length === 0 && this.input2Diffs.length === 0) { throw new BugIndicatingError('must have at least one diff'); } - - const input1Diff = - this.input1CombinedDiff || - new LineRangeMapping( - baseTextModel, - this.input2CombinedDiff!.inputRange, - input1TextModel, - this.input2CombinedDiff!.inputRange.delta(input1DeltaLineCount), - [] - ); - - const input2Diff = - this.input2CombinedDiff || - new LineRangeMapping( - baseTextModel, - this.input1CombinedDiff!.inputRange, - input1TextModel, - this.input1CombinedDiff!.inputRange.delta(input2DeltaLineCount), - [] - ); - - const results = LineRangeMapping.alignOriginalRange([input1Diff, input2Diff]); - this.baseRange = results[0].inputRange; - this.input1Range = results[0].outputRange; - this.input2Range = results[1].outputRange; } public getInputRange(inputNumber: 1 | 2): LineRange { return inputNumber === 1 ? this.input1Range : this.input2Range; } - public getInputDiffs(inputNumber: 1 | 2): readonly LineRangeMapping[] { + public getInputDiffs(inputNumber: 1 | 2): readonly DetailedLineRangeMapping[] { return inputNumber === 1 ? this.input1Diffs : this.input2Diffs; } @@ -439,14 +485,14 @@ export class ModifiedBaseRangeState { public readonly conflicting: boolean, ) { } - public getInput(inputNumber: 1 | 2): ToggleState { + public getInput(inputNumber: 1 | 2): InputState { if (this.conflicting) { - return ToggleState.conflicting; + return InputState.conflicting; } if (inputNumber === 1) { - return !this.input1 ? ToggleState.unset : this.input2First ? ToggleState.second : ToggleState.first; + return !this.input1 ? InputState.excluded : this.input2First ? InputState.second : InputState.first; } else { - return !this.input2 ? ToggleState.unset : !this.input2First ? ToggleState.second : ToggleState.first; + return !this.input2 ? InputState.excluded : !this.input2First ? InputState.second : InputState.first; } } @@ -472,14 +518,6 @@ export class ModifiedBaseRangeState { ); } - public toggleInput1(): ModifiedBaseRangeState { - return this.withInput1(!this.input1); - } - - public toggleInput2(): ModifiedBaseRangeState { - return this.withInput2(!this.input2); - } - public get isEmpty(): boolean { return !this.input1 && !this.input2; } @@ -499,122 +537,9 @@ export class ModifiedBaseRangeState { } } -export const enum ToggleState { - unset = 0, +export const enum InputState { + excluded = 0, first = 1, second = 2, conflicting = 3, } - -export class DocumentMapping { - public static fromDiffs( - diffs1: readonly LineRangeMapping[], - diffs2: readonly LineRangeMapping[], - inputLineCount: number - ): DocumentMapping { - const compareByStartLineNumber = compareBy( - (d) => d.inputRange.startLineNumber, - numberComparator - ); - - const diffs = diffs1 - .map((diff) => ({ source: 0 as 0 | 1, diff })) - .concat(diffs2.map((diff) => ({ source: 1 as const, diff }))); - - diffs.sort(compareBy((d) => d.diff, compareByStartLineNumber)); - - const currentDiffs = [new Array(), new Array()]; - const deltaFromBaseToInput = [0, 0]; - - const result = new Array(); - - function pushAndReset(baseRange: LineRange) { - const input1Range = LineRange.join(currentDiffs[0].map(d => d.outputRange)) || baseRange.delta(deltaFromBaseToInput[0]); - const input1BaseRange = LineRange.join(currentDiffs[0].map(d => d.inputRange)) || baseRange; - const mapping1 = new SimpleLineRangeMapping(input1BaseRange, input1Range); - - const input2Range = LineRange.join(currentDiffs[1].map(d => d.outputRange)) || baseRange.delta(deltaFromBaseToInput[1]); - const input2BaseRange = LineRange.join(currentDiffs[1].map(d => d.inputRange)) || baseRange; - const mapping2 = new SimpleLineRangeMapping(input2BaseRange, input2Range); - - result.push( - new SimpleLineRangeMapping( - mapping1.extendInputRange(currentInputRange!).outputRange, - mapping2.extendInputRange(currentInputRange!).outputRange - ) - ); - currentDiffs[0] = []; - currentDiffs[1] = []; - } - - let currentInputRange: LineRange | undefined; - - for (const diff of diffs) { - const range = diff.diff.inputRange; - if (currentInputRange && !currentInputRange.touches(range)) { - pushAndReset(currentInputRange); - currentInputRange = undefined; - } - deltaFromBaseToInput[diff.source] = - diff.diff.resultingDeltaFromOriginalToModified; - currentInputRange = currentInputRange ? currentInputRange.join(range) : range; - currentDiffs[diff.source].push(diff.diff); - } - if (currentInputRange) { - pushAndReset(currentInputRange); - } - - return new DocumentMapping(result, inputLineCount); - } - - public getOutputLine(inputLineNumber: number): number | SimpleLineRangeMapping { - const lastBefore = findLast(this.lineRangeMappings, r => r.inputRange.startLineNumber <= inputLineNumber); - if (lastBefore) { - if (lastBefore.inputRange.contains(inputLineNumber)) { - return lastBefore; - } - return inputLineNumber + lastBefore.outputRange.endLineNumberExclusive - lastBefore.inputRange.endLineNumberExclusive; - } - return inputLineNumber; - } - - public getInputLine(outputLineNumber: number): number | SimpleLineRangeMapping { - const lastBefore = findLast(this.lineRangeMappings, r => r.outputRange.startLineNumber <= outputLineNumber); - if (lastBefore) { - if (lastBefore.outputRange.contains(outputLineNumber)) { - return lastBefore; - } - return outputLineNumber + lastBefore.inputRange.endLineNumberExclusive - lastBefore.outputRange.endLineNumberExclusive; - } - return outputLineNumber; - } - - constructor( - public readonly lineRangeMappings: SimpleLineRangeMapping[], - public readonly inputLineCount: number - ) { } -} - -export class SimpleLineRangeMapping { - constructor( - public readonly inputRange: LineRange, - public readonly outputRange: LineRange - ) { } - - public extendInputRange(extendedInputRange: LineRange): SimpleLineRangeMapping { - if (!extendedInputRange.containsRange(this.inputRange)) { - throw new BugIndicatingError(); - } - - const startDelta = extendedInputRange.startLineNumber - this.inputRange.startLineNumber; - const endDelta = extendedInputRange.endLineNumberExclusive - this.inputRange.endLineNumberExclusive; - return new SimpleLineRangeMapping( - extendedInputRange, - new LineRange( - this.outputRange.startLineNumber + startDelta, - this.outputRange.lineCount - startDelta + endDelta - ) - ); - } -} - diff --git a/src/vs/workbench/contrib/mergeEditor/browser/textModelDiffs.ts b/src/vs/workbench/contrib/mergeEditor/browser/textModelDiffs.ts index cce7137adf1..040a755425c 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/textModelDiffs.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/textModelDiffs.ts @@ -8,14 +8,14 @@ import { BugIndicatingError } from 'vs/base/common/errors'; import { Disposable } from 'vs/base/common/lifecycle'; import { ITextModel } from 'vs/editor/common/model'; import { IObservable, ITransaction, ObservableValue, transaction } from 'vs/workbench/contrib/audioCues/browser/observable'; -import { LineRangeEdit, LineRange, LineRangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model'; +import { LineRangeEdit, LineRange, DetailedLineRangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model'; import { ReentrancyBarrier } from 'vs/workbench/contrib/mergeEditor/browser/utils'; import { IDiffComputer } from './diffComputer'; export class TextModelDiffs extends Disposable { private updateCount = 0; private readonly _state = new ObservableValue(TextModelDiffState.initializing, 'LiveDiffState'); - private readonly _diffs = new ObservableValue([], 'LiveDiffs'); + private readonly _diffs = new ObservableValue([], 'LiveDiffs'); private readonly barrier = new ReentrancyBarrier(); @@ -35,7 +35,7 @@ export class TextModelDiffs extends Disposable { return this._state; } - public get diffs(): IObservable { + public get diffs(): IObservable { return this._diffs; } @@ -78,7 +78,7 @@ export class TextModelDiffs extends Disposable { } } - public removeDiffs(diffToRemoves: LineRangeMapping[], transaction: ITransaction | undefined): void { + public removeDiffs(diffToRemoves: DetailedLineRangeMapping[], transaction: ITransaction | undefined): void { this.ensureUpToDate(); diffToRemoves.sort(compareBy((d) => d.inputRange.startLineNumber, numberComparator)); @@ -114,16 +114,16 @@ export class TextModelDiffs extends Disposable { public applyEditRelativeToOriginal(edit: LineRangeEdit, transaction: ITransaction | undefined): void { this.ensureUpToDate(); - const editMapping = new LineRangeMapping( - this.baseTextModel, + const editMapping = new DetailedLineRangeMapping( edit.range, - this.textModel, - new LineRange(edit.range.startLineNumber, edit.newLines.length) + this.baseTextModel, + new LineRange(edit.range.startLineNumber, edit.newLines.length), + this.textModel ); let firstAfter = false; let delta = 0; - const newDiffs = new Array(); + const newDiffs = new Array(); for (const diff of this.diffs.get()) { if (diff.inputRange.touches(edit.range)) { throw new BugIndicatingError('Edit must be conflict free.'); @@ -154,11 +154,11 @@ export class TextModelDiffs extends Disposable { this._diffs.set(newDiffs, transaction, TextModelDiffChangeReason.other); } - public findTouchingDiffs(baseRange: LineRange): LineRangeMapping[] { + public findTouchingDiffs(baseRange: LineRange): DetailedLineRangeMapping[] { return this.diffs.get().filter(d => d.inputRange.touches(baseRange)); } - private getResultLine(lineNumber: number): number | LineRangeMapping { + private getResultLine(lineNumber: number): number | DetailedLineRangeMapping { let offset = 0; for (const diff of this.diffs.get()) { if (diff.inputRange.contains(lineNumber) || diff.inputRange.endLineNumberExclusive === lineNumber) { @@ -200,5 +200,5 @@ export const enum TextModelDiffState { export interface ITextModelDiffsState { state: TextModelDiffState; - diffs: LineRangeMapping[]; + diffs: DetailedLineRangeMapping[]; } From f4ca89503ef2bae923f8fbff6b43b402e8d11f43 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Thu, 16 Jun 2022 12:38:08 -0400 Subject: [PATCH 099/175] Fix #151817 (#152356) --- .../workbench/contrib/files/browser/views/explorerViewer.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 3fd683669db..4064696d2bd 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -762,8 +762,9 @@ export class FilesFilter implements ITreeFilter { // Add newly visited .gitignore files to the ignore tree if (stat.name === '.gitignore') { this.processIgnoreFile(stat.root.resource.toString(), stat.resource, false); - // Never hide .gitignore files - return true; + // Never hide .gitignore files if explorer.excludeGitIgnore setting is enabled + // We can tell it's enabled if there is an ignore tree for the workspace root + return !!this.ignoreTreesPerRoot.get(stat.root.resource.toString()); } return this.isVisible(stat, parentVisibility); From d21ab95de19e067e2e7b67ee9c5d59fcd73710dc Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 16 Jun 2022 19:55:08 +0200 Subject: [PATCH 100/175] Splits up files of 3wm. --- .../mergeEditor/browser/commands/commands.ts | 130 +++++ .../browser/commands/devCommands.ts | 149 +++++ .../browser/mergeEditor.contribution.ts | 271 +-------- .../mergeEditor/browser/mergeEditorInput.ts | 2 +- .../contrib/mergeEditor/browser/model.ts | 545 ------------------ .../browser/{ => model}/diffComputer.ts | 3 +- .../mergeEditor/browser/model/editing.ts | 70 +++ .../mergeEditor/browser/model/lineRange.ts | 106 ++++ .../mergeEditor/browser/model/mapping.ts | 266 +++++++++ .../browser/{ => model}/mergeEditorModel.ts | 45 +- .../browser/model/modifiedBaseRange.ts | 145 +++++ .../browser/{ => model}/textModelDiffs.ts | 4 +- .../contrib/mergeEditor/browser/utils.ts | 10 +- .../browser/view/codeEditorView.ts | 309 ++++++++++ .../mergeEditor/browser/{ => view}/colors.ts | 0 .../browser/{ => view}/editorGutter.ts | 4 +- .../browser/{ => view}/media/mergeEditor.css | 0 .../browser/{ => view}/mergeEditor.ts | 406 +------------ 18 files changed, 1261 insertions(+), 1204 deletions(-) create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/commands/commands.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/commands/devCommands.ts delete mode 100644 src/vs/workbench/contrib/mergeEditor/browser/model.ts rename src/vs/workbench/contrib/mergeEditor/browser/{ => model}/diffComputer.ts (96%) create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/model/editing.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/model/lineRange.ts create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/model/mapping.ts rename src/vs/workbench/contrib/mergeEditor/browser/{ => model}/mergeEditorModel.ts (86%) create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange.ts rename src/vs/workbench/contrib/mergeEditor/browser/{ => model}/textModelDiffs.ts (96%) create mode 100644 src/vs/workbench/contrib/mergeEditor/browser/view/codeEditorView.ts rename src/vs/workbench/contrib/mergeEditor/browser/{ => view}/colors.ts (100%) rename src/vs/workbench/contrib/mergeEditor/browser/{ => view}/editorGutter.ts (99%) rename src/vs/workbench/contrib/mergeEditor/browser/{ => view}/media/mergeEditor.css (100%) rename src/vs/workbench/contrib/mergeEditor/browser/{ => view}/mergeEditor.ts (53%) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/commands/commands.ts b/src/vs/workbench/contrib/mergeEditor/browser/commands/commands.ts new file mode 100644 index 00000000000..6feb020989e --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/commands/commands.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 { Codicon } from 'vs/base/common/codicons'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { localize } from 'vs/nls'; +import { Action2, MenuId } from 'vs/platform/actions/common/actions'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { MergeEditorInput, MergeEditorInputData } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; +import { ctxIsMergeEditor, ctxUsesColumnLayout, MergeEditor } from 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; + +export class OpenMergeEditor extends Action2 { + + constructor() { + super({ + id: '_open.mergeEditor', + title: localize('title', "Open Merge Editor"), + }); + } + run(accessor: ServicesAccessor, ...args: any[]): void { + const validatedArgs = IRelaxedOpenArgs.validate(args[0]); + + const instaService = accessor.get(IInstantiationService); + const input = instaService.createInstance( + MergeEditorInput, + validatedArgs.ancestor, + validatedArgs.input1, + validatedArgs.input2, + validatedArgs.output, + ); + accessor.get(IEditorService).openEditor(input); + } +} + +namespace IRelaxedOpenArgs { + function toUri(obj: unknown): URI { + if (typeof obj === 'string') { + return URI.parse(obj, true); + } else if (obj && typeof obj === 'object') { + return URI.revive(obj); + } + throw new TypeError('invalid argument'); + } + + function isUriComponents(obj: unknown): obj is UriComponents { + if (!obj || typeof obj !== 'object') { + return false; + } + return typeof (obj).scheme === 'string' + && typeof (obj).authority === 'string' + && typeof (obj).path === 'string' + && typeof (obj).query === 'string' + && typeof (obj).fragment === 'string'; + } + + function toInputResource(obj: unknown): MergeEditorInputData { + if (typeof obj === 'string') { + return new MergeEditorInputData(URI.parse(obj, true), undefined, undefined); + } + if (!obj || typeof obj !== 'object') { + throw new TypeError('invalid argument'); + } + + if (isUriComponents(obj)) { + return new MergeEditorInputData(URI.revive(obj), undefined, undefined); + } + + const uri = toUri((obj).uri); + const detail = (obj).detail; + const description = (obj).description; + return new MergeEditorInputData(uri, detail, description); + } + + export function validate(obj: unknown): IOpenEditorArgs { + if (!obj || typeof obj !== 'object') { + throw new TypeError('invalid argument'); + } + const ancestor = toUri((obj).ancestor); + const output = toUri((obj).output); + const input1 = toInputResource((obj).input1); + const input2 = toInputResource((obj).input2); + return { ancestor, input1, input2, output }; + } +} + +type IRelaxedInputData = { uri: UriComponents; detail?: string; description?: string }; + +type IRelaxedOpenArgs = { + ancestor: UriComponents | string; + input1: IRelaxedInputData | string; + input2: IRelaxedInputData | string; + output: UriComponents | string; +}; + +interface IOpenEditorArgs { + ancestor: URI; + input1: MergeEditorInputData; + input2: MergeEditorInputData; + output: URI; +} + +export class ToggleLayout extends Action2 { + constructor() { + super({ + id: 'merge.toggleLayout', + title: localize('toggle.title', "Switch to column view"), + icon: Codicon.layoutCentered, + toggled: { + condition: ctxUsesColumnLayout, + icon: Codicon.layoutPanel, + title: localize('toggle.title2', "Switch to 2 by 1 view"), + }, + menu: [{ + id: MenuId.EditorTitle, + when: ctxIsMergeEditor, + group: 'navigation' + }] + }); + } + + run(accessor: ServicesAccessor): void { + const { activeEditorPane } = accessor.get(IEditorService); + if (activeEditorPane instanceof MergeEditor) { + activeEditorPane.toggleLayout(); + } + } +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/commands/devCommands.ts b/src/vs/workbench/contrib/mergeEditor/browser/commands/devCommands.ts new file mode 100644 index 00000000000..45f0b293e7f --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/commands/devCommands.ts @@ -0,0 +1,149 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { VSBuffer } from 'vs/base/common/buffer'; +import { Codicon } from 'vs/base/common/codicons'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { localize } from 'vs/nls'; +import { Action2 } from 'vs/platform/actions/common/actions'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { MergeEditor } from 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkbenchFileService } from 'vs/workbench/services/files/common/files'; +import { URI } from 'vs/base/common/uri'; +import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; + +interface MergeEditorContents { + languageId: string; + base: string; + input1: string; + input2: string; + result: string; +} + +export class MergeEditorCopyContentsToJSON extends Action2 { + + constructor() { + super({ + id: 'merge.dev.copyContents', + title: localize('merge.dev.copyContents', "Developer Merge Editor: Copy Contents of Inputs, Base and Result as JSON"), + icon: Codicon.layoutCentered, + f1: true, + }); + } + + run(accessor: ServicesAccessor): void { + const { activeEditorPane } = accessor.get(IEditorService); + const clipboardService = accessor.get(IClipboardService); + const notificationService = accessor.get(INotificationService); + + if (!(activeEditorPane instanceof MergeEditor)) { + notificationService.info({ + name: localize('mergeEditor.name', 'Merge Editor'), + message: localize('mergeEditor.noActiveMergeEditor', "No active merge editor") + }); + return; + } + const model = activeEditorPane.model; + if (!model) { + return; + } + const contents: MergeEditorContents = { + languageId: model.result.getLanguageId(), + base: model.base.getValue(), + input1: model.input1.getValue(), + input2: model.input2.getValue(), + result: model.result.getValue(), + }; + const jsonStr = JSON.stringify(contents, undefined, 4); + clipboardService.writeText(jsonStr); + + notificationService.info({ + name: localize('mergeEditor.name', 'Merge Editor'), + message: localize('mergeEditor.successfullyCopiedMergeEditorContents', "Successfully copied merge editor contents"), + }); + } +} + +export class MergeEditorOpenContents extends Action2 { + + constructor() { + super({ + id: 'merge.dev.openContents', + title: localize('merge.dev.openContents', "Developer Merge Editor: Open Contents of Inputs, Base and Result from JSON"), + icon: Codicon.layoutCentered, + f1: true, + }); + } + + async run(accessor: ServicesAccessor): Promise { + const service = accessor.get(IWorkbenchFileService); + const instaService = accessor.get(IInstantiationService); + const editorService = accessor.get(IEditorService); + const inputService = accessor.get(IQuickInputService); + const clipboardService = accessor.get(IClipboardService); + const textModelService = accessor.get(ITextModelService); + + const result = await inputService.input({ + prompt: localize('mergeEditor.enterJSON', 'Enter JSON'), + value: await clipboardService.readText(), + }); + if (!result) { + return; + } + + const content: MergeEditorContents = JSON.parse(result); + + const scheme = 'merge-editor-dev'; + + let provider = service.getProvider(scheme) as InMemoryFileSystemProvider | undefined; + if (!provider) { + provider = new InMemoryFileSystemProvider(); + service.registerProvider(scheme, provider); + } + + const baseUri = URI.from({ scheme, path: '/ancestor' }); + const input1Uri = URI.from({ scheme, path: '/input1' }); + const input2Uri = URI.from({ scheme, path: '/input2' }); + const resultUri = URI.from({ scheme, path: '/result' }); + + function writeFile(uri: URI, content: string): Promise { + return provider!.writeFile(uri, VSBuffer.fromString(content).buffer, { create: true, overwrite: true, unlock: true }); + } + + await Promise.all([ + writeFile(baseUri, content.base), + writeFile(input1Uri, content.input1), + writeFile(input2Uri, content.input2), + writeFile(resultUri, content.result), + ]); + + async function setLanguageId(uri: URI, languageId: string): Promise { + const ref = await textModelService.createModelReference(uri); + ref.object.textEditorModel.setMode(languageId); + ref.dispose(); + } + + await Promise.all([ + setLanguageId(baseUri, content.languageId), + setLanguageId(input1Uri, content.languageId), + setLanguageId(input2Uri, content.languageId), + setLanguageId(resultUri, content.languageId), + ]); + + const input = instaService.createInstance( + MergeEditorInput, + baseUri, + { uri: input1Uri, description: 'Input 1', detail: '(from JSON)' }, + { uri: input2Uri, description: 'Input 2', detail: '(from JSON)' }, + resultUri, + ); + editorService.openEditor(input); + } +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts index 1b3bd8141bc..be139f7608b 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts @@ -3,28 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - import { localize } from 'vs/nls'; -import { URI, UriComponents } from 'vs/base/common/uri'; -import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; +import { registerAction2 } from 'vs/platform/actions/common/actions'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { Registry } from 'vs/platform/registry/common/platform'; import { EditorPaneDescriptor, IEditorPaneRegistry } from 'vs/workbench/browser/editor'; import { EditorExtensions, IEditorFactoryRegistry } from 'vs/workbench/common/editor'; -import { ctxIsMergeEditor, ctxUsesColumnLayout, MergeEditor } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditor'; -import { MergeEditorInput, MergeEditorInputData } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { OpenMergeEditor, ToggleLayout } from 'vs/workbench/contrib/mergeEditor/browser/commands/commands'; +import { MergeEditorCopyContentsToJSON, MergeEditorOpenContents } from 'vs/workbench/contrib/mergeEditor/browser/commands/devCommands'; +import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; +import { MergeEditor } from 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor'; import { MergeEditorSerializer } from './mergeEditorSerializer'; -import { Codicon } from 'vs/base/common/codicons'; -import { IWorkbenchFileService } from 'vs/workbench/services/files/common/files'; -import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; -import { VSBuffer } from 'vs/base/common/buffer'; -import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import './colors'; Registry.as(EditorExtensions.EditorPane).registerEditorPane( EditorPaneDescriptor.create( @@ -42,250 +31,8 @@ Registry.as(EditorExtensions.EditorFactory).registerEdit MergeEditorSerializer ); -registerAction2(class ToggleLayout extends Action2 { +registerAction2(ToggleLayout); +registerAction2(OpenMergeEditor); - constructor() { - super({ - id: 'merge.toggleLayout', - title: localize('toggle.title', "Switch to column view"), - icon: Codicon.layoutCentered, - toggled: { - condition: ctxUsesColumnLayout, - icon: Codicon.layoutPanel, - title: localize('toggle.title2', "Switch to 2 by 1 view"), - }, - menu: [{ - id: MenuId.EditorTitle, - when: ctxIsMergeEditor, - group: 'navigation' - }] - }); - } - - run(accessor: ServicesAccessor): void { - const { activeEditorPane } = accessor.get(IEditorService); - if (activeEditorPane instanceof MergeEditor) { - activeEditorPane.toggleLayout(); - } - } -}); - -registerAction2(class Open extends Action2 { - - constructor() { - super({ - id: '_open.mergeEditor', - title: localize('title', "Open Merge Editor"), - }); - } - run(accessor: ServicesAccessor, ...args: any[]): void { - const validatedArgs = IRelaxedOpenArgs.validate(args[0]); - - const instaService = accessor.get(IInstantiationService); - const input = instaService.createInstance( - MergeEditorInput, - validatedArgs.ancestor, - validatedArgs.input1, - validatedArgs.input2, - validatedArgs.output, - ); - accessor.get(IEditorService).openEditor(input); - } - -}); - -namespace IRelaxedOpenArgs { - function toUri(obj: unknown): URI { - if (typeof obj === 'string') { - return URI.parse(obj, true); - } else if (obj && typeof obj === 'object') { - return URI.revive(obj); - } - throw new TypeError('invalid argument'); - } - - function isUriComponents(obj: unknown): obj is UriComponents { - if (!obj || typeof obj !== 'object') { - return false; - } - return typeof (obj).scheme === 'string' - && typeof (obj).authority === 'string' - && typeof (obj).path === 'string' - && typeof (obj).query === 'string' - && typeof (obj).fragment === 'string'; - } - - function toInputResource(obj: unknown): MergeEditorInputData { - if (typeof obj === 'string') { - return new MergeEditorInputData(URI.parse(obj, true), undefined, undefined); - } - if (!obj || typeof obj !== 'object') { - throw new TypeError('invalid argument'); - } - - if (isUriComponents(obj)) { - return new MergeEditorInputData(URI.revive(obj), undefined, undefined); - } - - const uri = toUri((obj).uri); - const detail = (obj).detail; - const description = (obj).description; - return new MergeEditorInputData(uri, detail, description); - } - - export function validate(obj: unknown): IOpenEditorArgs { - if (!obj || typeof obj !== 'object') { - throw new TypeError('invalid argument'); - } - const ancestor = toUri((obj).ancestor); - const output = toUri((obj).output); - const input1 = toInputResource((obj).input1); - const input2 = toInputResource((obj).input2); - return { ancestor, input1, input2, output }; - } -} - -type IRelaxedInputData = { uri: UriComponents; detail?: string; description?: string }; - -type IRelaxedOpenArgs = { - ancestor: UriComponents | string; - input1: IRelaxedInputData | string; - input2: IRelaxedInputData | string; - output: UriComponents | string; -}; - -interface IOpenEditorArgs { - ancestor: URI; - input1: MergeEditorInputData; - input2: MergeEditorInputData; - output: URI; -} - -registerAction2(class extends Action2 { - - constructor() { - super({ - id: 'merge.dev.copyContents', - title: localize('merge.dev.copyContents', "Developer Merge Editor: Copy Contents of Inputs, Base and Result as JSON"), - icon: Codicon.layoutCentered, - f1: true, - }); - } - - run(accessor: ServicesAccessor): void { - const { activeEditorPane } = accessor.get(IEditorService); - const clipboardService = accessor.get(IClipboardService); - const notificationService = accessor.get(INotificationService); - - if (!(activeEditorPane instanceof MergeEditor)) { - notificationService.info({ - name: localize('mergeEditor.name', 'Merge Editor'), - message: localize('mergeEditor.noActiveMergeEditor', "No active merge editor") - }); - return; - } - const model = activeEditorPane.model; - if (!model) { - return; - } - const contents: MergeEditorContents = { - languageId: model.result.getLanguageId(), - base: model.base.getValue(), - input1: model.input1.getValue(), - input2: model.input2.getValue(), - result: model.result.getValue(), - }; - const jsonStr = JSON.stringify(contents, undefined, 4); - clipboardService.writeText(jsonStr); - - notificationService.info({ - name: localize('mergeEditor.name', 'Merge Editor'), - message: localize('mergeEditor.successfullyCopiedMergeEditorContents', "Successfully copied merge editor contents"), - }); - } -}); - -registerAction2(class extends Action2 { - - constructor() { - super({ - id: 'merge.dev.openContents', - title: localize('merge.dev.openContents', "Developer Merge Editor: Open Contents of Inputs, Base and Result from JSON"), - icon: Codicon.layoutCentered, - f1: true, - }); - } - - async run(accessor: ServicesAccessor): Promise { - const service = accessor.get(IWorkbenchFileService); - const instaService = accessor.get(IInstantiationService); - const editorService = accessor.get(IEditorService); - const inputService = accessor.get(IQuickInputService); - const clipboardService = accessor.get(IClipboardService); - const textModelService = accessor.get(ITextModelService); - - const result = await inputService.input({ - prompt: localize('mergeEditor.enterJSON', 'Enter JSON'), - value: await clipboardService.readText(), - }); - if (!result) { - return; - } - - const content: MergeEditorContents = JSON.parse(result); - - const scheme = 'merge-editor-dev'; - - let provider = service.getProvider(scheme) as InMemoryFileSystemProvider | undefined; - if (!provider) { - provider = new InMemoryFileSystemProvider(); - service.registerProvider(scheme, provider); - } - - const baseUri = URI.from({ scheme, path: '/ancestor' }); - const input1Uri = URI.from({ scheme, path: '/input1' }); - const input2Uri = URI.from({ scheme, path: '/input2' }); - const resultUri = URI.from({ scheme, path: '/result' }); - - function writeFile(uri: URI, content: string): Promise { - return provider!.writeFile(uri, VSBuffer.fromString(content).buffer, { create: true, overwrite: true, unlock: true }); - } - - await Promise.all([ - writeFile(baseUri, content.base), - writeFile(input1Uri, content.input1), - writeFile(input2Uri, content.input2), - writeFile(resultUri, content.result), - ]); - - async function setLanguageId(uri: URI, languageId: string): Promise { - const ref = await textModelService.createModelReference(uri); - ref.object.textEditorModel.setMode(languageId); - ref.dispose(); - } - - await Promise.all([ - setLanguageId(baseUri, content.languageId), - setLanguageId(input1Uri, content.languageId), - setLanguageId(input2Uri, content.languageId), - setLanguageId(resultUri, content.languageId), - ]); - - const input = instaService.createInstance( - MergeEditorInput, - baseUri, - { uri: input1Uri, description: 'Input 1', detail: '(from JSON)' }, - { uri: input2Uri, description: 'Input 2', detail: '(from JSON)' }, - resultUri, - ); - editorService.openEditor(input); - } -}); - -interface MergeEditorContents { - languageId: string; - base: string; - input1: string; - input2: string; - result: string; -} +registerAction2(MergeEditorCopyContentsToJSON); +registerAction2(MergeEditorOpenContents); diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts index efa8b0360e0..b96047e2cfd 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts @@ -14,7 +14,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { IUntypedEditorInput, EditorInputCapabilities } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { AbstractTextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput'; -import { MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorModel'; +import { MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITextFileEditorModel, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model.ts b/src/vs/workbench/contrib/mergeEditor/browser/model.ts deleted file mode 100644 index 5880c4a5275..00000000000 --- a/src/vs/workbench/contrib/mergeEditor/browser/model.ts +++ /dev/null @@ -1,545 +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 { Comparator, compareBy, equals, findLast, numberComparator } from 'vs/base/common/arrays'; -import { BugIndicatingError } from 'vs/base/common/errors'; -import { Range } from 'vs/editor/common/core/range'; -import { ITextModel } from 'vs/editor/common/model'; -import { concatArrays } from 'vs/workbench/contrib/mergeEditor/browser/utils'; - -export class LineRange { - public static readonly compareByStart: Comparator = compareBy(l => l.startLineNumber, numberComparator); - - public static join(ranges: LineRange[]): LineRange | undefined { - if (ranges.length === 0) { - return undefined; - } - - let startLineNumber = Number.MAX_SAFE_INTEGER; - let endLineNumber = 0; - for (const range of ranges) { - startLineNumber = Math.min(startLineNumber, range.startLineNumber); - endLineNumber = Math.max(endLineNumber, range.startLineNumber + range.lineCount); - } - return new LineRange(startLineNumber, endLineNumber - startLineNumber); - } - - static fromLineNumbers(startLineNumber: number, endExclusiveLineNumber: number): LineRange { - return new LineRange(startLineNumber, endExclusiveLineNumber - startLineNumber); - } - - constructor( - public readonly startLineNumber: number, - public readonly lineCount: number - ) { - if (lineCount < 0) { - throw new BugIndicatingError(); - } - } - - public join(other: LineRange): LineRange { - return new LineRange(Math.min(this.startLineNumber, other.startLineNumber), Math.max(this.endLineNumberExclusive, other.endLineNumberExclusive) - this.startLineNumber); - } - - public get endLineNumberExclusive(): number { - return this.startLineNumber + this.lineCount; - } - - public get isEmpty(): boolean { - return this.lineCount === 0; - } - - /** - * Returns false if there is at least one line between `this` and `other`. - */ - public touches(other: LineRange): boolean { - return ( - this.endLineNumberExclusive >= other.startLineNumber && - other.endLineNumberExclusive >= this.startLineNumber - ); - } - - public isAfter(modifiedRange: LineRange): boolean { - return this.startLineNumber >= modifiedRange.endLineNumberExclusive; - } - - public delta(lineDelta: number): LineRange { - return new LineRange(this.startLineNumber + lineDelta, this.lineCount); - } - - public toString() { - return `[${this.startLineNumber},${this.endLineNumberExclusive})`; - } - - public equals(originalRange: LineRange) { - return this.startLineNumber === originalRange.startLineNumber && this.lineCount === originalRange.lineCount; - } - - public contains(lineNumber: number): boolean { - return this.startLineNumber <= lineNumber && lineNumber < this.endLineNumberExclusive; - } - - public deltaEnd(delta: number): LineRange { - return new LineRange(this.startLineNumber, this.lineCount + delta); - } - - public deltaStart(lineDelta: number): LineRange { - return new LineRange(this.startLineNumber + lineDelta, this.lineCount - lineDelta); - } - - public getLines(model: ITextModel): string[] { - const result = new Array(this.lineCount); - for (let i = 0; i < this.lineCount; i++) { - result[i] = model.getLineContent(this.startLineNumber + i); - } - return result; - } - - public containsRange(range: LineRange): boolean { - return this.startLineNumber <= range.startLineNumber && range.endLineNumberExclusive <= this.endLineNumberExclusive; - } - - public toRange(): Range { - return new Range(this.startLineNumber, 1, this.endLineNumberExclusive, 1); - } -} - -export class LineRangeMapping { - public static join(mappings: readonly LineRangeMapping[]): LineRangeMapping | undefined { - return mappings.reduce((acc, cur) => acc ? acc.join(cur) : cur, undefined); - } - - constructor( - public readonly inputRange: LineRange, - public readonly outputRange: LineRange - ) { } - - public extendInputRange(extendedInputRange: LineRange): LineRangeMapping { - if (!extendedInputRange.containsRange(this.inputRange)) { - throw new BugIndicatingError(); - } - - const startDelta = extendedInputRange.startLineNumber - this.inputRange.startLineNumber; - const endDelta = extendedInputRange.endLineNumberExclusive - this.inputRange.endLineNumberExclusive; - return new LineRangeMapping( - extendedInputRange, - new LineRange( - this.outputRange.startLineNumber + startDelta, - this.outputRange.lineCount - startDelta + endDelta - ) - ); - } - - public join(other: LineRangeMapping): LineRangeMapping { - return new LineRangeMapping( - this.inputRange.join(other.inputRange), - this.outputRange.join(other.outputRange) - ); - } - - public get resultingDeltaFromOriginalToModified(): number { - return this.outputRange.endLineNumberExclusive - this.inputRange.endLineNumberExclusive; - } - - public toString(): string { - return `${this.inputRange.toString()} -> ${this.outputRange.toString()}`; - } - - public addOutputLineDelta(delta: number): LineRangeMapping { - return new LineRangeMapping( - this.inputRange, - this.outputRange.delta(delta) - ); - } -} - -export class MappingAlignment { - public static compute( - fromBaseToInput1: readonly T[], - fromBaseToInput2: readonly T[] - ): MappingAlignment[] { - const compareByStartLineNumber = compareBy( - (d) => d.inputRange.startLineNumber, - numberComparator - ); - - const combinedDiffs = concatArrays( - fromBaseToInput1.map((diff) => ({ source: 0 as const, diff })), - fromBaseToInput2.map((diff) => ({ source: 1 as const, diff })) - ).sort(compareBy((d) => d.diff, compareByStartLineNumber)); - - const currentDiffs = [new Array(), new Array()]; - const deltaFromBaseToInput = [0, 0]; - - const alignments = new Array>(); - - function pushAndReset(baseRange: LineRange) { - const mapping1 = LineRangeMapping.join(currentDiffs[0]) || new LineRangeMapping(baseRange, baseRange.delta(deltaFromBaseToInput[0])); - const mapping2 = LineRangeMapping.join(currentDiffs[1]) || new LineRangeMapping(baseRange, baseRange.delta(deltaFromBaseToInput[1])); - - alignments.push( - new MappingAlignment( - currentInputRange!, - mapping1.extendInputRange(currentInputRange!).outputRange, - currentDiffs[0], - mapping2.extendInputRange(currentInputRange!).outputRange, - currentDiffs[1] - ) - ); - currentDiffs[0] = []; - currentDiffs[1] = []; - } - - let currentInputRange: LineRange | undefined; - - for (const diff of combinedDiffs) { - const range = diff.diff.inputRange; - if (currentInputRange && !currentInputRange.touches(range)) { - pushAndReset(currentInputRange); - currentInputRange = undefined; - } - deltaFromBaseToInput[diff.source] = - diff.diff.resultingDeltaFromOriginalToModified; - currentInputRange = currentInputRange ? currentInputRange.join(range) : range; - currentDiffs[diff.source].push(diff.diff); - } - if (currentInputRange) { - pushAndReset(currentInputRange); - } - - return alignments; - } - - constructor( - public readonly baseRange: LineRange, - public readonly input1Range: LineRange, - public readonly input1LineMappings: T[], - public readonly input2Range: LineRange, - public readonly input2LineMappings: T[], - ) { - } - - toString(): string { - return `${this.input1Range} <- ${this.baseRange} -> ${this.input2Range}`; - } -} - -export class DocumentMapping { - public static betweenOutputs( - inputToOutput1: readonly LineRangeMapping[], - inputToOutput2: readonly LineRangeMapping[], - inputLineCount: number - ): DocumentMapping { - const alignments = MappingAlignment.compute(inputToOutput1, inputToOutput2); - const mappings = alignments.map((m) => new LineRangeMapping(m.input1Range, m.input2Range)); - return new DocumentMapping(mappings, inputLineCount); - } - - public getOutputLine(inputLineNumber: number): number | LineRangeMapping { - const lastBefore = findLast(this.lineRangeMappings, r => r.inputRange.startLineNumber <= inputLineNumber); - if (lastBefore) { - if (lastBefore.inputRange.contains(inputLineNumber)) { - return lastBefore; - } - return inputLineNumber + lastBefore.outputRange.endLineNumberExclusive - lastBefore.inputRange.endLineNumberExclusive; - } - return inputLineNumber; - } - - public getInputLine(outputLineNumber: number): number | LineRangeMapping { - const lastBefore = findLast(this.lineRangeMappings, r => r.outputRange.startLineNumber <= outputLineNumber); - if (lastBefore) { - if (lastBefore.outputRange.contains(outputLineNumber)) { - return lastBefore; - } - return outputLineNumber + lastBefore.inputRange.endLineNumberExclusive - lastBefore.outputRange.endLineNumberExclusive; - } - return outputLineNumber; - } - - constructor( - public readonly lineRangeMappings: LineRangeMapping[], - public readonly inputLineCount: number - ) { } -} - -/** - * Represents an edit, expressed in whole lines: - * At (before) {@link LineRange.startLineNumber}, delete {@link LineRange.lineCount} many lines and insert {@link newLines}. -*/ -export class LineRangeEdit { - constructor( - public readonly range: LineRange, - public readonly newLines: string[] - ) { } - - public equals(other: LineRangeEdit): boolean { - return this.range.equals(other.range) && equals(this.newLines, other.newLines); - } - - public apply(model: ITextModel): void { - new LineEdits([this]).apply(model); - } -} - -export class RangeEdit { - constructor( - public readonly range: Range, - public readonly newText: string - ) { } - - public equals(other: RangeEdit): boolean { - return Range.equalsRange(this.range, other.range) && this.newText === other.newText; - } -} - -export class LineEdits { - constructor(public readonly edits: readonly LineRangeEdit[]) { } - - public apply(model: ITextModel): void { - model.pushEditOperations( - null, - this.edits.map((e) => { - if (e.range.endLineNumberExclusive <= model.getLineCount()) { - return { - range: new Range(e.range.startLineNumber, 1, e.range.endLineNumberExclusive, 1), - text: e.newLines.map(s => s + '\n').join(''), - }; - } - - if (e.range.startLineNumber === 1) { - return { - range: new Range(1, 1, model.getLineCount(), Number.MAX_SAFE_INTEGER), - text: e.newLines.join('\n'), - }; - } - - return { - range: new Range(e.range.startLineNumber - 1, Number.MAX_SAFE_INTEGER, model.getLineCount(), Number.MAX_SAFE_INTEGER), - text: e.newLines.map(s => '\n' + s).join(''), - }; - }), - () => null - ); - } -} - -export class DetailedLineRangeMapping extends LineRangeMapping { - public static override join(mappings: readonly DetailedLineRangeMapping[]): DetailedLineRangeMapping | undefined { - return mappings.reduce((acc, cur) => acc ? acc.join(cur) : cur, undefined); - } - - public readonly rangeMappings: readonly RangeMapping[]; - - constructor( - inputRange: LineRange, - public readonly inputTextModel: ITextModel, - outputRange: LineRange, - public readonly outputTextModel: ITextModel, - rangeMappings?: readonly RangeMapping[], - ) { - super(inputRange, outputRange); - - this.rangeMappings = rangeMappings || [new RangeMapping(this.inputRange.toRange(), this.outputRange.toRange())]; - } - - public override addOutputLineDelta(delta: number): DetailedLineRangeMapping { - return new DetailedLineRangeMapping( - this.inputRange, - this.inputTextModel, - this.outputRange.delta(delta), - this.outputTextModel, - this.rangeMappings.map(d => d.addOutputLineDelta(delta)) - ); - } - - public override join(other: DetailedLineRangeMapping): DetailedLineRangeMapping { - return new DetailedLineRangeMapping( - this.inputRange.join(other.inputRange), - this.inputTextModel, - this.outputRange.join(other.outputRange), - this.outputTextModel, - ); - } - - public getLineEdit(): LineRangeEdit { - return new LineRangeEdit(this.inputRange, this.getOutputLines()); - } - - public getReverseLineEdit(): LineRangeEdit { - return new LineRangeEdit(this.outputRange, this.getInputLines()); - } - - private getOutputLines(): string[] { - return this.outputRange.getLines(this.outputTextModel); - } - - private getInputLines(): string[] { - return this.inputRange.getLines(this.inputTextModel); - } -} - -export class RangeMapping { - constructor(public readonly inputRange: Range, public readonly outputRange: Range) { - } - - toString(): string { - function rangeToString(range: Range) { - // TODO@hediet make this the default Range.toString - return `[${range.startLineNumber}:${range.startColumn}, ${range.endLineNumber}:${range.endColumn})`; - } - - return `${rangeToString(this.inputRange)} -> ${rangeToString(this.outputRange)}`; - } - - addOutputLineDelta(deltaLines: number): RangeMapping { - return new RangeMapping( - this.inputRange, - new Range( - this.outputRange.startLineNumber + deltaLines, - this.outputRange.startColumn, - this.outputRange.endLineNumber + deltaLines, - this.outputRange.endColumn - ) - ); - } -} - -/** - * Describes modifications in input 1 and input 2 for a specific range in base. - * - * The UI offers a mechanism to either apply all changes from input 1 or input 2 or both. - * - * Immutable. -*/ -export class ModifiedBaseRange { - /** - * diffs1 and diffs2 together with the conflict relation form a bipartite graph. - * This method computes strongly connected components of that graph while maintaining the side of each diff. - */ - public static fromDiffs( - diffs1: readonly DetailedLineRangeMapping[], - diffs2: readonly DetailedLineRangeMapping[], - baseTextModel: ITextModel, - input1TextModel: ITextModel, - input2TextModel: ITextModel, - ): ModifiedBaseRange[] { - const alignments = MappingAlignment.compute(diffs1, diffs2); - return alignments.map( - (a) => - new ModifiedBaseRange( - a.baseRange, - baseTextModel, - a.input1Range, - input1TextModel, - a.input1LineMappings, - a.input2Range, - input2TextModel, - a.input2LineMappings - ) - ); - } - - public readonly input1CombinedDiff = DetailedLineRangeMapping.join(this.input1Diffs); - public readonly input2CombinedDiff = DetailedLineRangeMapping.join(this.input2Diffs); - - - constructor( - public readonly baseRange: LineRange, - public readonly baseTextModel: ITextModel, - public readonly input1Range: LineRange, - public readonly input1TextModel: ITextModel, - public readonly input1Diffs: readonly DetailedLineRangeMapping[], - public readonly input2Range: LineRange, - public readonly input2TextModel: ITextModel, - public readonly input2Diffs: readonly DetailedLineRangeMapping[], - ) { - if (this.input1Diffs.length === 0 && this.input2Diffs.length === 0) { - throw new BugIndicatingError('must have at least one diff'); - } - } - - public getInputRange(inputNumber: 1 | 2): LineRange { - return inputNumber === 1 ? this.input1Range : this.input2Range; - } - - public getInputDiffs(inputNumber: 1 | 2): readonly DetailedLineRangeMapping[] { - return inputNumber === 1 ? this.input1Diffs : this.input2Diffs; - } - - public get isConflicting(): boolean { - return this.input1Diffs.length > 0 && this.input2Diffs.length > 0; - } -} - -export class ModifiedBaseRangeState { - public static readonly default = new ModifiedBaseRangeState(false, false, false, false); - public static readonly conflicting = new ModifiedBaseRangeState(false, false, false, true); - - private constructor( - public readonly input1: boolean, - public readonly input2: boolean, - public readonly input2First: boolean, - public readonly conflicting: boolean, - ) { } - - public getInput(inputNumber: 1 | 2): InputState { - if (this.conflicting) { - return InputState.conflicting; - } - if (inputNumber === 1) { - return !this.input1 ? InputState.excluded : this.input2First ? InputState.second : InputState.first; - } else { - return !this.input2 ? InputState.excluded : !this.input2First ? InputState.second : InputState.first; - } - } - - public withInputValue(inputNumber: 1 | 2, value: boolean): ModifiedBaseRangeState { - return inputNumber === 1 ? this.withInput1(value) : this.withInput2(value); - } - - public withInput1(value: boolean): ModifiedBaseRangeState { - return new ModifiedBaseRangeState( - value, - this.input2, - value !== this.input2 ? this.input2 : this.input2First, - false, - ); - } - - public withInput2(value: boolean): ModifiedBaseRangeState { - return new ModifiedBaseRangeState( - this.input1, - value, - value !== this.input1 ? value : this.input2First, - false - ); - } - - public get isEmpty(): boolean { - return !this.input1 && !this.input2; - } - - public toString(): string { - const arr: ('1' | '2')[] = []; - if (this.input1) { - arr.push('1'); - } - if (this.input2) { - arr.push('2'); - } - if (this.input2First) { - arr.reverse(); - } - return arr.join(','); - } -} - -export const enum InputState { - excluded = 0, - first = 1, - second = 2, - conflicting = 3, -} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/diffComputer.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts similarity index 96% rename from src/vs/workbench/contrib/mergeEditor/browser/diffComputer.ts rename to src/vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts index 9e3d8083a40..452443bb0aa 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/diffComputer.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts @@ -8,7 +8,8 @@ import { Range } from 'vs/editor/common/core/range'; import { ICharChange, IDiffComputationResult, ILineChange } from 'vs/editor/common/diff/diffComputer'; import { ITextModel } from 'vs/editor/common/model'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; -import { LineRange, DetailedLineRangeMapping, RangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model'; +import { DetailedLineRangeMapping, RangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model/mapping'; +import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model/lineRange'; export interface IDiffComputer { computeDiff(textModel1: ITextModel, textModel2: ITextModel): Promise; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/editing.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/editing.ts new file mode 100644 index 00000000000..de2e9e2c6df --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/editing.ts @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { equals } from 'vs/base/common/arrays'; +import { Range } from 'vs/editor/common/core/range'; +import { ITextModel } from 'vs/editor/common/model'; +import { LineRange } from './lineRange'; + +/** + * Represents an edit, expressed in whole lines: + * At (before) {@link LineRange.startLineNumber}, delete {@link LineRange.lineCount} many lines and insert {@link newLines}. +*/ +export class LineRangeEdit { + constructor( + public readonly range: LineRange, + public readonly newLines: string[] + ) { } + + public equals(other: LineRangeEdit): boolean { + return this.range.equals(other.range) && equals(this.newLines, other.newLines); + } + + public apply(model: ITextModel): void { + new LineEdits([this]).apply(model); + } +} + +export class RangeEdit { + constructor( + public readonly range: Range, + public readonly newText: string + ) { } + + public equals(other: RangeEdit): boolean { + return Range.equalsRange(this.range, other.range) && this.newText === other.newText; + } +} + +export class LineEdits { + constructor(public readonly edits: readonly LineRangeEdit[]) { } + + public apply(model: ITextModel): void { + model.pushEditOperations( + null, + this.edits.map((e) => { + if (e.range.endLineNumberExclusive <= model.getLineCount()) { + return { + range: new Range(e.range.startLineNumber, 1, e.range.endLineNumberExclusive, 1), + text: e.newLines.map(s => s + '\n').join(''), + }; + } + + if (e.range.startLineNumber === 1) { + return { + range: new Range(1, 1, model.getLineCount(), Number.MAX_SAFE_INTEGER), + text: e.newLines.join('\n'), + }; + } + + return { + range: new Range(e.range.startLineNumber - 1, Number.MAX_SAFE_INTEGER, model.getLineCount(), Number.MAX_SAFE_INTEGER), + text: e.newLines.map(s => '\n' + s).join(''), + }; + }), + () => null + ); + } +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/lineRange.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/lineRange.ts new file mode 100644 index 00000000000..57730125f09 --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/lineRange.ts @@ -0,0 +1,106 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Comparator, compareBy, numberComparator } from 'vs/base/common/arrays'; +import { BugIndicatingError } from 'vs/base/common/errors'; +import { Range } from 'vs/editor/common/core/range'; +import { ITextModel } from 'vs/editor/common/model'; + +export class LineRange { + public static readonly compareByStart: Comparator = compareBy(l => l.startLineNumber, numberComparator); + + public static join(ranges: LineRange[]): LineRange | undefined { + if (ranges.length === 0) { + return undefined; + } + + let startLineNumber = Number.MAX_SAFE_INTEGER; + let endLineNumber = 0; + for (const range of ranges) { + startLineNumber = Math.min(startLineNumber, range.startLineNumber); + endLineNumber = Math.max(endLineNumber, range.startLineNumber + range.lineCount); + } + return new LineRange(startLineNumber, endLineNumber - startLineNumber); + } + + static fromLineNumbers(startLineNumber: number, endExclusiveLineNumber: number): LineRange { + return new LineRange(startLineNumber, endExclusiveLineNumber - startLineNumber); + } + + constructor( + public readonly startLineNumber: number, + public readonly lineCount: number + ) { + if (lineCount < 0) { + throw new BugIndicatingError(); + } + } + + public join(other: LineRange): LineRange { + return new LineRange(Math.min(this.startLineNumber, other.startLineNumber), Math.max(this.endLineNumberExclusive, other.endLineNumberExclusive) - this.startLineNumber); + } + + public get endLineNumberExclusive(): number { + return this.startLineNumber + this.lineCount; + } + + public get isEmpty(): boolean { + return this.lineCount === 0; + } + + /** + * Returns false if there is at least one line between `this` and `other`. + */ + public touches(other: LineRange): boolean { + return ( + this.endLineNumberExclusive >= other.startLineNumber && + other.endLineNumberExclusive >= this.startLineNumber + ); + } + + public isAfter(modifiedRange: LineRange): boolean { + return this.startLineNumber >= modifiedRange.endLineNumberExclusive; + } + + public delta(lineDelta: number): LineRange { + return new LineRange(this.startLineNumber + lineDelta, this.lineCount); + } + + public toString() { + return `[${this.startLineNumber},${this.endLineNumberExclusive})`; + } + + public equals(originalRange: LineRange) { + return this.startLineNumber === originalRange.startLineNumber && this.lineCount === originalRange.lineCount; + } + + public contains(lineNumber: number): boolean { + return this.startLineNumber <= lineNumber && lineNumber < this.endLineNumberExclusive; + } + + public deltaEnd(delta: number): LineRange { + return new LineRange(this.startLineNumber, this.lineCount + delta); + } + + public deltaStart(lineDelta: number): LineRange { + return new LineRange(this.startLineNumber + lineDelta, this.lineCount - lineDelta); + } + + public getLines(model: ITextModel): string[] { + const result = new Array(this.lineCount); + for (let i = 0; i < this.lineCount; i++) { + result[i] = model.getLineContent(this.startLineNumber + i); + } + return result; + } + + public containsRange(range: LineRange): boolean { + return this.startLineNumber <= range.startLineNumber && range.endLineNumberExclusive <= this.endLineNumberExclusive; + } + + public toRange(): Range { + return new Range(this.startLineNumber, 1, this.endLineNumberExclusive, 1); + } +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/mapping.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/mapping.ts new file mode 100644 index 00000000000..6cb52ce450e --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/mapping.ts @@ -0,0 +1,266 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { compareBy, findLast, numberComparator } from 'vs/base/common/arrays'; +import { BugIndicatingError } from 'vs/base/common/errors'; +import { Range } from 'vs/editor/common/core/range'; +import { ITextModel } from 'vs/editor/common/model'; +import { concatArrays } from 'vs/workbench/contrib/mergeEditor/browser/utils'; +import { LineRange } from './lineRange'; +import { LineRangeEdit } from './editing'; + +export class LineRangeMapping { + public static join(mappings: readonly LineRangeMapping[]): LineRangeMapping | undefined { + return mappings.reduce((acc, cur) => acc ? acc.join(cur) : cur, undefined); + } + + constructor( + public readonly inputRange: LineRange, + public readonly outputRange: LineRange + ) { } + + public extendInputRange(extendedInputRange: LineRange): LineRangeMapping { + if (!extendedInputRange.containsRange(this.inputRange)) { + throw new BugIndicatingError(); + } + + const startDelta = extendedInputRange.startLineNumber - this.inputRange.startLineNumber; + const endDelta = extendedInputRange.endLineNumberExclusive - this.inputRange.endLineNumberExclusive; + return new LineRangeMapping( + extendedInputRange, + new LineRange( + this.outputRange.startLineNumber + startDelta, + this.outputRange.lineCount - startDelta + endDelta + ) + ); + } + + public join(other: LineRangeMapping): LineRangeMapping { + return new LineRangeMapping( + this.inputRange.join(other.inputRange), + this.outputRange.join(other.outputRange) + ); + } + + public get resultingDeltaFromOriginalToModified(): number { + return this.outputRange.endLineNumberExclusive - this.inputRange.endLineNumberExclusive; + } + + public toString(): string { + return `${this.inputRange.toString()} -> ${this.outputRange.toString()}`; + } + + public addOutputLineDelta(delta: number): LineRangeMapping { + return new LineRangeMapping( + this.inputRange, + this.outputRange.delta(delta) + ); + } + + public getRange(direction: MappingDirection): LineRange { + return direction === MappingDirection.input ? this.inputRange : this.outputRange; + } +} + +export function getOppositeDirection(direction: MappingDirection): MappingDirection { + return direction === MappingDirection.input ? MappingDirection.output : MappingDirection.input; +} + +export const enum MappingDirection { + input = 0, + output = 1, +} + +export class MappingAlignment { + public static compute( + fromBaseToInput1: readonly T[], + fromBaseToInput2: readonly T[] + ): MappingAlignment[] { + const compareByStartLineNumber = compareBy( + (d) => d.inputRange.startLineNumber, + numberComparator + ); + + const combinedDiffs = concatArrays( + fromBaseToInput1.map((diff) => ({ source: 0 as const, diff })), + fromBaseToInput2.map((diff) => ({ source: 1 as const, diff })) + ).sort(compareBy((d) => d.diff, compareByStartLineNumber)); + + const currentDiffs = [new Array(), new Array()]; + const deltaFromBaseToInput = [0, 0]; + + const alignments = new Array>(); + + function pushAndReset(baseRange: LineRange) { + const mapping1 = LineRangeMapping.join(currentDiffs[0]) || new LineRangeMapping(baseRange, baseRange.delta(deltaFromBaseToInput[0])); + const mapping2 = LineRangeMapping.join(currentDiffs[1]) || new LineRangeMapping(baseRange, baseRange.delta(deltaFromBaseToInput[1])); + + alignments.push( + new MappingAlignment( + currentInputRange!, + mapping1.extendInputRange(currentInputRange!).outputRange, + currentDiffs[0], + mapping2.extendInputRange(currentInputRange!).outputRange, + currentDiffs[1] + ) + ); + currentDiffs[0] = []; + currentDiffs[1] = []; + } + + let currentInputRange: LineRange | undefined; + + for (const diff of combinedDiffs) { + const range = diff.diff.inputRange; + if (currentInputRange && !currentInputRange.touches(range)) { + pushAndReset(currentInputRange); + currentInputRange = undefined; + } + deltaFromBaseToInput[diff.source] = + diff.diff.resultingDeltaFromOriginalToModified; + currentInputRange = currentInputRange ? currentInputRange.join(range) : range; + currentDiffs[diff.source].push(diff.diff); + } + if (currentInputRange) { + pushAndReset(currentInputRange); + } + + return alignments; + } + + constructor( + public readonly baseRange: LineRange, + public readonly input1Range: LineRange, + public readonly input1LineMappings: T[], + public readonly input2Range: LineRange, + public readonly input2LineMappings: T[], + ) { + } + + toString(): string { + return `${this.input1Range} <- ${this.baseRange} -> ${this.input2Range}`; + } +} + +export class DocumentMapping { + public static betweenOutputs( + inputToOutput1: readonly LineRangeMapping[], + inputToOutput2: readonly LineRangeMapping[], + inputLineCount: number + ): DocumentMapping { + const alignments = MappingAlignment.compute(inputToOutput1, inputToOutput2); + const mappings = alignments.map((m) => new LineRangeMapping(m.input1Range, m.input2Range)); + return new DocumentMapping(mappings, inputLineCount); + } + + public getMappingContaining(lineNumber: number, containingDirection: MappingDirection): LineRangeMapping { + const mapTo = getOppositeDirection(containingDirection); + const lastBefore = findLast(this.lineRangeMappings, r => r.getRange(containingDirection).startLineNumber <= lineNumber); + if (lastBefore) { + if (lastBefore.getRange(containingDirection).contains(lineNumber)) { + return lastBefore; + } + return new LineRangeMapping( + new LineRange(lineNumber, 1), + new LineRange( + lineNumber + + lastBefore.getRange(mapTo).endLineNumberExclusive - + lastBefore.getRange(containingDirection).endLineNumberExclusive, + 1 + ) + ); + } + return new LineRangeMapping( + new LineRange(lineNumber, 1), + new LineRange(lineNumber, 1) + ); + } + + constructor( + public readonly lineRangeMappings: LineRangeMapping[], + public readonly inputLineCount: number + ) { } +} + +export class DetailedLineRangeMapping extends LineRangeMapping { + public static override join(mappings: readonly DetailedLineRangeMapping[]): DetailedLineRangeMapping | undefined { + return mappings.reduce((acc, cur) => acc ? acc.join(cur) : cur, undefined); + } + + public readonly rangeMappings: readonly RangeMapping[]; + + constructor( + inputRange: LineRange, + public readonly inputTextModel: ITextModel, + outputRange: LineRange, + public readonly outputTextModel: ITextModel, + rangeMappings?: readonly RangeMapping[], + ) { + super(inputRange, outputRange); + + this.rangeMappings = rangeMappings || [new RangeMapping(this.inputRange.toRange(), this.outputRange.toRange())]; + } + + public override addOutputLineDelta(delta: number): DetailedLineRangeMapping { + return new DetailedLineRangeMapping( + this.inputRange, + this.inputTextModel, + this.outputRange.delta(delta), + this.outputTextModel, + this.rangeMappings.map(d => d.addOutputLineDelta(delta)) + ); + } + + public override join(other: DetailedLineRangeMapping): DetailedLineRangeMapping { + return new DetailedLineRangeMapping( + this.inputRange.join(other.inputRange), + this.inputTextModel, + this.outputRange.join(other.outputRange), + this.outputTextModel, + ); + } + + public getLineEdit(): LineRangeEdit { + return new LineRangeEdit(this.inputRange, this.getOutputLines()); + } + + public getReverseLineEdit(): LineRangeEdit { + return new LineRangeEdit(this.outputRange, this.getInputLines()); + } + + private getOutputLines(): string[] { + return this.outputRange.getLines(this.outputTextModel); + } + + private getInputLines(): string[] { + return this.inputRange.getLines(this.inputTextModel); + } +} + +export class RangeMapping { + constructor(public readonly inputRange: Range, public readonly outputRange: Range) { + } + + toString(): string { + function rangeToString(range: Range) { + // TODO@hediet make this the default Range.toString + return `[${range.startLineNumber}:${range.startColumn}, ${range.endLineNumber}:${range.endColumn})`; + } + + return `${rangeToString(this.inputRange)} -> ${rangeToString(this.outputRange)}`; + } + + addOutputLineDelta(deltaLines: number): RangeMapping { + return new RangeMapping( + this.inputRange, + new Range( + this.outputRange.startLineNumber + deltaLines, + this.outputRange.startColumn, + this.outputRange.endLineNumber + deltaLines, + this.outputRange.endColumn + ) + ); + } +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts similarity index 86% rename from src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts rename to src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts index 135d6a2d64a..65bcadcc743 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts @@ -13,10 +13,13 @@ import { ITextModel } from 'vs/editor/common/model'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { EditorModel } from 'vs/workbench/common/editor/editorModel'; import { autorunHandleChanges, derivedObservable, derivedObservableWithCache, IObservable, ITransaction, keepAlive, ObservableValue, transaction, waitForState } from 'vs/workbench/contrib/audioCues/browser/observable'; -import { EditorWorkerServiceDiffComputer } from 'vs/workbench/contrib/mergeEditor/browser/diffComputer'; -import { LineRangeEdit, LineRange, ModifiedBaseRange, ModifiedBaseRangeState, RangeEdit, DetailedLineRangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model'; -import { TextModelDiffChangeReason, TextModelDiffs, TextModelDiffState } from 'vs/workbench/contrib/mergeEditor/browser/textModelDiffs'; +import { EditorWorkerServiceDiffComputer } from 'vs/workbench/contrib/mergeEditor/browser/model/diffComputer'; +import { DetailedLineRangeMapping, DocumentMapping, LineRangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model/mapping'; +import { LineRangeEdit, RangeEdit } from 'vs/workbench/contrib/mergeEditor/browser/model/editing'; +import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model/lineRange'; +import { TextModelDiffChangeReason, TextModelDiffs, TextModelDiffState } from 'vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs'; import { concatArrays, leftJoin, elementAtOrUndefined } from 'vs/workbench/contrib/mergeEditor/browser/utils'; +import { ModifiedBaseRange, ModifiedBaseRangeState } from './modifiedBaseRange'; export const enum MergeEditorModelState { initializing = 1, @@ -73,6 +76,40 @@ export class MergeEditorModel extends EditorModel { return map; }); + public readonly input1ResultMapping = derivedObservable('input1ResultMapping', reader => { + const resultDiffs = this.resultDiffs.read(reader); + const modifiedBaseRanges = DocumentMapping.betweenOutputs(this.input1LinesDiffs.read(reader), resultDiffs, this.input1.getLineCount()); + + return new DocumentMapping( + modifiedBaseRanges.lineRangeMappings.map((m) => + m.inputRange.isEmpty || m.outputRange.isEmpty + ? new LineRangeMapping( + m.inputRange.deltaStart(-1), + m.outputRange.deltaStart(-1) + ) + : m + ), + modifiedBaseRanges.inputLineCount + ); + }); + + public readonly input2ResultMapping = derivedObservable('input2ResultMapping', reader => { + const resultDiffs = this.resultDiffs.read(reader); + const modifiedBaseRanges = DocumentMapping.betweenOutputs(this.input2LinesDiffs.read(reader), resultDiffs, this.input2.getLineCount()); + + return new DocumentMapping( + modifiedBaseRanges.lineRangeMappings.map((m) => + m.inputRange.isEmpty || m.outputRange.isEmpty + ? new LineRangeMapping( + m.inputRange.deltaStart(-1), + m.outputRange.deltaStart(-1) + ) + : m + ), + modifiedBaseRanges.inputLineCount + ); + }); + constructor( readonly base: ITextModel, readonly input1: ITextModel, @@ -87,6 +124,8 @@ export class MergeEditorModel extends EditorModel { super(); this._register(keepAlive(this.modifiedBaseRangeStateStores)); + this._register(keepAlive(this.input1ResultMapping)); + this._register(keepAlive(this.input2ResultMapping)); this._register( autorunHandleChanges( diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange.ts new file mode 100644 index 00000000000..efd6b87fbec --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange.ts @@ -0,0 +1,145 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BugIndicatingError } from 'vs/base/common/errors'; +import { ITextModel } from 'vs/editor/common/model'; +import { DetailedLineRangeMapping, MappingAlignment } from 'vs/workbench/contrib/mergeEditor/browser/model/mapping'; +import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model/lineRange'; + +/** + * Describes modifications in input 1 and input 2 for a specific range in base. + * + * The UI offers a mechanism to either apply all changes from input 1 or input 2 or both. + * + * Immutable. +*/ +export class ModifiedBaseRange { + /** + * diffs1 and diffs2 together with the conflict relation form a bipartite graph. + * This method computes strongly connected components of that graph while maintaining the side of each diff. + */ + public static fromDiffs( + diffs1: readonly DetailedLineRangeMapping[], + diffs2: readonly DetailedLineRangeMapping[], + baseTextModel: ITextModel, + input1TextModel: ITextModel, + input2TextModel: ITextModel + ): ModifiedBaseRange[] { + const alignments = MappingAlignment.compute(diffs1, diffs2); + return alignments.map( + (a) => new ModifiedBaseRange( + a.baseRange, + baseTextModel, + a.input1Range, + input1TextModel, + a.input1LineMappings, + a.input2Range, + input2TextModel, + a.input2LineMappings + ) + ); + } + + public readonly input1CombinedDiff = DetailedLineRangeMapping.join(this.input1Diffs); + public readonly input2CombinedDiff = DetailedLineRangeMapping.join(this.input2Diffs); + + + constructor( + public readonly baseRange: LineRange, + public readonly baseTextModel: ITextModel, + public readonly input1Range: LineRange, + public readonly input1TextModel: ITextModel, + public readonly input1Diffs: readonly DetailedLineRangeMapping[], + public readonly input2Range: LineRange, + public readonly input2TextModel: ITextModel, + public readonly input2Diffs: readonly DetailedLineRangeMapping[] + ) { + if (this.input1Diffs.length === 0 && this.input2Diffs.length === 0) { + throw new BugIndicatingError('must have at least one diff'); + } + } + + public getInputRange(inputNumber: 1 | 2): LineRange { + return inputNumber === 1 ? this.input1Range : this.input2Range; + } + + public getInputDiffs(inputNumber: 1 | 2): readonly DetailedLineRangeMapping[] { + return inputNumber === 1 ? this.input1Diffs : this.input2Diffs; + } + + public get isConflicting(): boolean { + return this.input1Diffs.length > 0 && this.input2Diffs.length > 0; + } +} + +export class ModifiedBaseRangeState { + public static readonly default = new ModifiedBaseRangeState(false, false, false, false); + public static readonly conflicting = new ModifiedBaseRangeState(false, false, false, true); + + private constructor( + public readonly input1: boolean, + public readonly input2: boolean, + public readonly input2First: boolean, + public readonly conflicting: boolean + ) { } + + public getInput(inputNumber: 1 | 2): InputState { + if (this.conflicting) { + return InputState.conflicting; + } + if (inputNumber === 1) { + return !this.input1 ? InputState.excluded : this.input2First ? InputState.second : InputState.first; + } else { + return !this.input2 ? InputState.excluded : !this.input2First ? InputState.second : InputState.first; + } + } + + public withInputValue(inputNumber: 1 | 2, value: boolean): ModifiedBaseRangeState { + return inputNumber === 1 ? this.withInput1(value) : this.withInput2(value); + } + + public withInput1(value: boolean): ModifiedBaseRangeState { + return new ModifiedBaseRangeState( + value, + this.input2, + value !== this.input2 ? this.input2 : this.input2First, + false + ); + } + + public withInput2(value: boolean): ModifiedBaseRangeState { + return new ModifiedBaseRangeState( + this.input1, + value, + value !== this.input1 ? value : this.input2First, + false + ); + } + + public get isEmpty(): boolean { + return !this.input1 && !this.input2; + } + + public toString(): string { + const arr: ('1' | '2')[] = []; + if (this.input1) { + arr.push('1'); + } + if (this.input2) { + arr.push('2'); + } + if (this.input2First) { + arr.reverse(); + } + return arr.join(','); + } +} + +export const enum InputState { + excluded = 0, + first = 1, + second = 2, + conflicting = 3 +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/textModelDiffs.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts similarity index 96% rename from src/vs/workbench/contrib/mergeEditor/browser/textModelDiffs.ts rename to src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts index 040a755425c..d3b9e29c4df 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/textModelDiffs.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts @@ -8,7 +8,9 @@ import { BugIndicatingError } from 'vs/base/common/errors'; import { Disposable } from 'vs/base/common/lifecycle'; import { ITextModel } from 'vs/editor/common/model'; import { IObservable, ITransaction, ObservableValue, transaction } from 'vs/workbench/contrib/audioCues/browser/observable'; -import { LineRangeEdit, LineRange, DetailedLineRangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model'; +import { DetailedLineRangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model/mapping'; +import { LineRangeEdit } from 'vs/workbench/contrib/mergeEditor/browser/model/editing'; +import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model/lineRange'; import { ReentrancyBarrier } from 'vs/workbench/contrib/mergeEditor/browser/utils'; import { IDiffComputer } from './diffComputer'; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/utils.ts b/src/vs/workbench/contrib/mergeEditor/browser/utils.ts index f6859c527e8..dcdc184e704 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/utils.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/utils.ts @@ -53,21 +53,21 @@ export class ReentrancyBarrier { } } -export function n(tag: TTag): never; -export function n( +export function h(tag: TTag): never; +export function h( tag: TTag, attributes: { $: TId } ): Record>; -export function n)[]>( +export function h)[]>( tag: TTag, children: T ): (ArrayToObj & Record<'root', TagToElement>) extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never; -export function n)[]>( +export function h)[]>( tag: TTag, attributes: { $: TId }, children: T ): (ArrayToObj & Record>) extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never; -export function n(tag: string, ...args: [] | [attributes: { $: string } | Record, children?: any[]] | [children: any[]]): Record { +export function h(tag: string, ...args: [] | [attributes: { $: string } | Record, children?: any[]] | [children: any[]]): Record { let attributes: Record; let children: (Record | HTMLElement)[] | undefined; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/codeEditorView.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/codeEditorView.ts new file mode 100644 index 00000000000..c977770909f --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/codeEditorView.ts @@ -0,0 +1,309 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IView, IViewSize } from 'vs/base/browser/ui/grid/grid'; +import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; +import { Toggle } from 'vs/base/browser/ui/toggle/toggle'; +import { CompareResult } from 'vs/base/common/arrays'; +import { Codicon } from 'vs/base/common/codicons'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { noBreakWhitespace } from 'vs/base/common/strings'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { Range } from 'vs/editor/common/core/range'; +import { IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; +import { localize } from 'vs/nls'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { DEFAULT_EDITOR_MAX_DIMENSIONS, DEFAULT_EDITOR_MIN_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor'; +import { autorun, derivedObservable, IObservable, ITransaction, ObservableValue, transaction } from 'vs/workbench/contrib/audioCues/browser/observable'; +import { MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel'; +import { InputState } from 'vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange'; +import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model/lineRange'; +import { applyObservableDecorations, join, h, setStyle } from 'vs/workbench/contrib/mergeEditor/browser/utils'; +import { EditorGutter, IGutterItemInfo, IGutterItemView } from './editorGutter'; + +export interface ICodeEditorViewOptions { + readonly: boolean; +} + + +abstract class CodeEditorView extends Disposable { + private readonly _model = new ObservableValue(undefined, 'model'); + readonly model: IObservable = this._model; + + protected readonly htmlElements = h('div.code-view', [ + h('div.title', { $: 'title' }), + h('div.container', [ + h('div.gutter', { $: 'gutterDiv' }), + h('div', { $: 'editor' }), + ]), + ]); + + private readonly _onDidViewChange = new Emitter(); + + public readonly view: IView = { + element: this.htmlElements.root, + minimumWidth: DEFAULT_EDITOR_MIN_DIMENSIONS.width, + maximumWidth: DEFAULT_EDITOR_MAX_DIMENSIONS.width, + minimumHeight: DEFAULT_EDITOR_MIN_DIMENSIONS.height, + maximumHeight: DEFAULT_EDITOR_MAX_DIMENSIONS.height, + onDidChange: this._onDidViewChange.event, + layout: (width: number, height: number, top: number, left: number) => { + setStyle(this.htmlElements.root, { width, height, top, left }); + this.editor.layout({ + width: width - this.htmlElements.gutterDiv.clientWidth, + height: height - this.htmlElements.title.clientHeight, + }); + } + // preferredWidth?: number | undefined; + // preferredHeight?: number | undefined; + // priority?: LayoutPriority | undefined; + // snap?: boolean | undefined; + }; + + private readonly _title = new IconLabel(this.htmlElements.title, { supportIcons: true }); + private readonly _detail = new IconLabel(this.htmlElements.title, { supportIcons: true }); + + public readonly editor = this.instantiationService.createInstance( + CodeEditorWidget, + this.htmlElements.editor, + { + minimap: { enabled: false }, + readOnly: this._options.readonly, + glyphMargin: false, + lineNumbersMinChars: 2, + }, + { contributions: [] } + ); + + constructor( + private readonly _options: ICodeEditorViewOptions, + @IInstantiationService + private readonly instantiationService: IInstantiationService + ) { + super(); + } + + public setModel( + model: MergeEditorModel, + textModel: ITextModel, + title: string, + description: string | undefined, + detail: string | undefined + ): void { + this.editor.setModel(textModel); + this._title.setLabel(title, description); + this._detail.setLabel('', detail); + + this._model.set(model, undefined); + } +} +export class InputCodeEditorView extends CodeEditorView { + private readonly decorations = derivedObservable('decorations', reader => { + const model = this.model.read(reader); + if (!model) { + return []; + } + const result = new Array(); + for (const m of model.modifiedBaseRanges.read(reader)) { + const range = m.getInputRange(this.inputNumber); + if (!range.isEmpty) { + result.push({ + range: new Range(range.startLineNumber, 1, range.endLineNumberExclusive - 1, 1), + options: { + isWholeLine: true, + className: `merge-editor-modified-base-range-input${this.inputNumber}`, + description: 'Base Range Projection' + } + }); + + const inputDiffs = m.getInputDiffs(this.inputNumber); + for (const diff of inputDiffs) { + if (diff.rangeMappings) { + for (const d of diff.rangeMappings) { + result.push({ + range: d.outputRange, + options: { + className: `merge-editor-diff-input${this.inputNumber}`, + description: 'Base Range Projection' + } + }); + } + } + } + } + } + return result; + }); + + constructor( + public readonly inputNumber: 1 | 2, + options: ICodeEditorViewOptions, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(options, instantiationService); + + this._register(applyObservableDecorations(this.editor, this.decorations)); + + this._register( + new EditorGutter(this.editor, this.htmlElements.gutterDiv, { + getIntersectingGutterItems: (range, reader) => { + const model = this.model.read(reader); + if (!model) { return []; } + return model.modifiedBaseRanges.read(reader) + .filter((r) => r.getInputDiffs(this.inputNumber).length > 0) + .map((baseRange, idx) => ({ + id: idx.toString(), + additionalHeightInPx: 0, + offsetInPx: 0, + range: baseRange.getInputRange(this.inputNumber), + enabled: model.isUpToDate, + toggleState: derivedObservable('toggle', (reader) => model + .getState(baseRange) + .read(reader) + .getInput(this.inputNumber) + ), + setState: (value, tx) => model.setState( + baseRange, + model + .getState(baseRange) + .get() + .withInputValue(this.inputNumber, value), + tx + ), + })); + }, + createView: (item, target) => new MergeConflictGutterItemView(item, target), + }) + ); + } +} + +interface ModifiedBaseRangeGutterItemInfo extends IGutterItemInfo { + enabled: IObservable; + toggleState: IObservable; + setState(value: boolean, tx: ITransaction): void; +} + +class MergeConflictGutterItemView extends Disposable implements IGutterItemView { + private readonly item = new ObservableValue(undefined, 'item'); + + constructor(item: ModifiedBaseRangeGutterItemInfo, private readonly target: HTMLElement) { + super(); + + this.item.set(item, undefined); + + target.classList.add('merge-accept-gutter-marker'); + + const checkBox = new Toggle({ isChecked: false, title: localize('acceptMerge', "Accept Merge"), icon: Codicon.check }); + checkBox.domNode.classList.add('accept-conflict-group'); + + this._register( + autorun((reader) => { + const item = this.item.read(reader)!; + const value = item.toggleState.read(reader); + const iconMap: Record = { + [InputState.excluded]: { icon: undefined, checked: false }, + [InputState.conflicting]: { icon: Codicon.circleFilled, checked: false }, + [InputState.first]: { icon: Codicon.check, checked: true }, + [InputState.second]: { icon: Codicon.checkAll, checked: true }, + }; + checkBox.setIcon(iconMap[value].icon); + checkBox.checked = iconMap[value].checked; + + if (!item.enabled.read(reader)) { + checkBox.disable(); + } else { + checkBox.enable(); + } + }, 'Update Toggle State') + ); + + this._register(checkBox.onChange(() => { + transaction(tx => { + this.item.get()!.setState(checkBox.checked, tx); + }); + })); + + target.appendChild(h('div.background', [noBreakWhitespace]).root); + target.appendChild( + h('div.checkbox', [h('div.checkbox-background', [checkBox.domNode])]).root + ); + } + + layout(top: number, height: number, viewTop: number, viewHeight: number): void { + this.target.classList.remove('multi-line'); + this.target.classList.remove('single-line'); + this.target.classList.add(height > 30 ? 'multi-line' : 'single-line'); + } + + update(baseRange: ModifiedBaseRangeGutterItemInfo): void { + this.item.set(baseRange, undefined); + } +} + +export class ResultCodeEditorView extends CodeEditorView { + private readonly decorations = derivedObservable('decorations', reader => { + const model = this.model.read(reader); + if (!model) { + return []; + } + const result = new Array(); + + const baseRangeWithStoreAndTouchingDiffs = join( + model.modifiedBaseRanges.read(reader), + model.resultDiffs.read(reader), + (baseRange, diff) => baseRange.baseRange.touches(diff.inputRange) + ? CompareResult.neitherLessOrGreaterThan + : LineRange.compareByStart( + baseRange.baseRange, + diff.inputRange + ) + ); + + for (const m of baseRangeWithStoreAndTouchingDiffs) { + for (const r of m.rights) { + const range = r.outputRange; + + const state = m.left ? model.getState(m.left).read(reader) : undefined; + + if (!range.isEmpty) { + result.push({ + range: new Range(range.startLineNumber, 1, range.endLineNumberExclusive - 1, 1), + options: { + isWholeLine: true, + // TODO + className: (() => { + if (state) { + if (state.input1 && !state.input2) { + return 'merge-editor-modified-base-range-input1'; + } + if (state.input2 && !state.input1) { + return 'merge-editor-modified-base-range-input2'; + } + if (state.input1 && state.input2) { + return 'merge-editor-modified-base-range-combination'; + } + } + return 'merge-editor-modified-base-range'; + })(), + description: 'Result Diff' + } + }); + } + } + } + return result; + }); + + constructor( + options: ICodeEditorViewOptions, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(options, instantiationService); + + this._register(applyObservableDecorations(this.editor, this.decorations)); + } +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/colors.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/colors.ts similarity index 100% rename from src/vs/workbench/contrib/mergeEditor/browser/colors.ts rename to src/vs/workbench/contrib/mergeEditor/browser/view/colors.ts diff --git a/src/vs/workbench/contrib/mergeEditor/browser/editorGutter.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/editorGutter.ts similarity index 99% rename from src/vs/workbench/contrib/mergeEditor/browser/editorGutter.ts rename to src/vs/workbench/contrib/mergeEditor/browser/view/editorGutter.ts index 63ec1e1c2c1..fee958633e5 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/editorGutter.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/editorGutter.ts @@ -7,7 +7,7 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { autorun, IReader, observableFromEvent, ObservableValue } from 'vs/workbench/contrib/audioCues/browser/observable'; -import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model'; +import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model/lineRange'; export class EditorGutter extends Disposable { private readonly scrollTop = observableFromEvent( @@ -132,10 +132,12 @@ export interface IGutterItemProvider { export interface IGutterItemInfo { id: string; range: LineRange; + /* // To accommodate view zones: offsetInPx: number; additionalHeightInPx: number; + */ } export interface IGutterItemView extends IDisposable { diff --git a/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css b/src/vs/workbench/contrib/mergeEditor/browser/view/media/mergeEditor.css similarity index 100% rename from src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css rename to src/vs/workbench/contrib/mergeEditor/browser/view/media/mergeEditor.css diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts similarity index 53% rename from src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts rename to src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts index 9c89ef79a0e..f8acdc3deaa 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts @@ -3,28 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import 'vs/css!./media/mergeEditor'; +import './colors'; import { $, Dimension, reset } from 'vs/base/browser/dom'; -import { Direction, Grid, IView, IViewSize, SerializableGrid } from 'vs/base/browser/ui/grid/grid'; -import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; +import { Direction, Grid, IView, SerializableGrid } from 'vs/base/browser/ui/grid/grid'; import { Orientation, Sizing } from 'vs/base/browser/ui/splitview/splitview'; -import { Toggle } from 'vs/base/browser/ui/toggle/toggle'; import { IAction } from 'vs/base/common/actions'; -import { CompareResult } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { Codicon } from 'vs/base/common/codicons'; import { Color } from 'vs/base/common/color'; import { BugIndicatingError } from 'vs/base/common/errors'; -import { Emitter } from 'vs/base/common/event'; -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { noBreakWhitespace } from 'vs/base/common/strings'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import 'vs/css!./media/mergeEditor'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { IEditorOptions as ICodeEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { Range } from 'vs/editor/common/core/range'; import { ScrollType } from 'vs/editor/common/editorCommon'; -import { IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration'; import { localize } from 'vs/nls'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; @@ -39,20 +32,19 @@ 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 { FloatingClickWidget } from 'vs/workbench/browser/codeeditor'; -import { DEFAULT_EDITOR_MAX_DIMENSIONS, DEFAULT_EDITOR_MIN_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor'; import { AbstractTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; import { IEditorOpenContext } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { applyTextEditorOptions } from 'vs/workbench/common/editor/editorOptions'; -import { autorun, autorunWithStore, derivedObservable, IObservable, ITransaction, keepAlive, ObservableValue, transaction } from 'vs/workbench/contrib/audioCues/browser/observable'; +import { autorunWithStore } from 'vs/workbench/contrib/audioCues/browser/observable'; import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; -import { MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorModel'; -import { DocumentMapping, LineRange, LineRangeMapping, InputState } from 'vs/workbench/contrib/mergeEditor/browser/model'; -import { applyObservableDecorations, join, n, ReentrancyBarrier, setStyle } from 'vs/workbench/contrib/mergeEditor/browser/utils'; +import { MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel'; +import { DocumentMapping, getOppositeDirection, MappingDirection } from 'vs/workbench/contrib/mergeEditor/browser/model/mapping'; +import { ReentrancyBarrier } from 'vs/workbench/contrib/mergeEditor/browser/utils'; import { settingsSashBorder } from 'vs/workbench/contrib/preferences/common/settingsEditorColorRegistry'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { EditorGutter, IGutterItemInfo, IGutterItemView } from './editorGutter'; +import { InputCodeEditorView, ResultCodeEditorView } from './codeEditorView'; export const ctxIsMergeEditor = new RawContextKey('isMergeEditor', false); export const ctxUsesColumnLayout = new RawContextKey('mergeEditorUsesColumnLayout', false); @@ -103,56 +95,12 @@ export class MergeEditor extends AbstractTextEditor { const reentrancyBarrier = new ReentrancyBarrier(); - const input1ResultMapping = derivedObservable('input1ResultMapping', reader => { - const model = this.input1View.model.read(reader); - if (!model) { - return undefined; - } - const resultDiffs = model.resultDiffs.read(reader); - const modifiedBaseRanges = DocumentMapping.betweenOutputs(model.input1LinesDiffs.read(reader), resultDiffs, model.input1.getLineCount()); - - return new DocumentMapping( - modifiedBaseRanges.lineRangeMappings.map((m) => - m.inputRange.isEmpty || m.outputRange.isEmpty - ? new LineRangeMapping( - m.inputRange.deltaStart(-1), - m.outputRange.deltaStart(-1) - ) - : m - ), - modifiedBaseRanges.inputLineCount - ); - }); - const input2ResultMapping = derivedObservable('input2ResultMapping', reader => { - const model = this.input2View.model.read(reader); - if (!model) { - return undefined; - } - const resultDiffs = model.resultDiffs.read(reader); - const modifiedBaseRanges = DocumentMapping.betweenOutputs(model.input2LinesDiffs.read(reader), resultDiffs, model.input2.getLineCount()); - - return new DocumentMapping( - modifiedBaseRanges.lineRangeMappings.map((m) => - m.inputRange.isEmpty || m.outputRange.isEmpty - ? new LineRangeMapping( - m.inputRange.deltaStart(-1), - m.outputRange.deltaStart(-1) - ) - : m - ), - modifiedBaseRanges.inputLineCount - ); - }); - - this._register(keepAlive(input1ResultMapping)); - this._register(keepAlive(input2ResultMapping)); - this._store.add( this.input1View.editor.onDidScrollChange( reentrancyBarrier.makeExclusive((c) => { if (c.scrollTopChanged) { - const mapping = input1ResultMapping.get(); - synchronizeScrolling(this.input1View.editor, this.inputResultView.editor, mapping, 1); + const mapping = this.model?.input1ResultMapping.get(); + synchronizeScrolling(this.input1View.editor, this.inputResultView.editor, mapping, MappingDirection.input); this.input2View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); } }) @@ -162,8 +110,8 @@ export class MergeEditor extends AbstractTextEditor { this.input2View.editor.onDidScrollChange( reentrancyBarrier.makeExclusive((c) => { if (c.scrollTopChanged) { - const mapping = input2ResultMapping.get(); - synchronizeScrolling(this.input2View.editor, this.inputResultView.editor, mapping, 1); + const mapping = this.model?.input2ResultMapping.get(); + synchronizeScrolling(this.input2View.editor, this.inputResultView.editor, mapping, MappingDirection.input); this.input1View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); } }) @@ -173,10 +121,10 @@ export class MergeEditor extends AbstractTextEditor { this.inputResultView.editor.onDidScrollChange( reentrancyBarrier.makeExclusive((c) => { if (c.scrollTopChanged) { - const mapping1 = input1ResultMapping.get(); - synchronizeScrolling(this.inputResultView.editor, this.input1View.editor, mapping1, 2); - const mapping2 = input2ResultMapping.get(); - synchronizeScrolling(this.inputResultView.editor, this.input2View.editor, mapping2, 2); + const mapping1 = this.model?.input1ResultMapping.get(); + synchronizeScrolling(this.inputResultView.editor, this.input1View.editor, mapping1, MappingDirection.output); + const mapping2 = this.model?.input2ResultMapping.get(); + synchronizeScrolling(this.inputResultView.editor, this.input2View.editor, mapping2, MappingDirection.output); } }) ) @@ -412,7 +360,7 @@ export class MergeEditor extends AbstractTextEditor { } } -function synchronizeScrolling(scrollingEditor: CodeEditorWidget, targetEditor: CodeEditorWidget, mapping: DocumentMapping | undefined, sourceNumber: 1 | 2) { +function synchronizeScrolling(scrollingEditor: CodeEditorWidget, targetEditor: CodeEditorWidget, mapping: DocumentMapping | undefined, source: MappingDirection) { if (!mapping) { return; } @@ -423,30 +371,9 @@ function synchronizeScrolling(scrollingEditor: CodeEditorWidget, targetEditor: C } const topLineNumber = visibleRanges[0].startLineNumber - 1; - let sourceRange: LineRange; - let targetRange: LineRange; - - if (sourceNumber === 1) { - const number = mapping.getOutputLine(topLineNumber); - if (typeof number === 'number') { - sourceRange = new LineRange(topLineNumber, 1); - targetRange = new LineRange(number, 1); - } else { - sourceRange = number.inputRange; - targetRange = number.outputRange; - } - } else { - const number = mapping.getInputLine(topLineNumber); - if (typeof number === 'number') { - sourceRange = new LineRange(topLineNumber, 1); - targetRange = new LineRange(number, 1); - } else { - sourceRange = number.outputRange; - targetRange = number.inputRange; - } - } - - // sourceRange contains topLineNumber! + const result = mapping.getMappingContaining(topLineNumber, source); + const sourceRange = result.getRange(source); + const targetRange = result.getRange(getOppositeDirection(source)); const resultStartTopPx = targetEditor.getTopForLineNumber(targetRange.startLineNumber); const resultEndPx = targetEditor.getTopForLineNumber(targetRange.endLineNumberExclusive); @@ -459,294 +386,3 @@ function synchronizeScrolling(scrollingEditor: CodeEditorWidget, targetEditor: C targetEditor.setScrollTop(resultScrollPosition, ScrollType.Immediate); } - -interface ICodeEditorViewOptions { - readonly: boolean; -} - - -abstract class CodeEditorView extends Disposable { - private readonly _model = new ObservableValue(undefined, 'model'); - readonly model: IObservable = this._model; - - protected readonly htmlElements = n('div.code-view', [ - n('div.title', { $: 'title' }), - n('div.container', [ - n('div.gutter', { $: 'gutterDiv' }), - n('div', { $: 'editor' }), - ]), - ]); - - private readonly _onDidViewChange = new Emitter(); - - public readonly view: IView = { - element: this.htmlElements.root, - minimumWidth: DEFAULT_EDITOR_MIN_DIMENSIONS.width, - maximumWidth: DEFAULT_EDITOR_MAX_DIMENSIONS.width, - minimumHeight: DEFAULT_EDITOR_MIN_DIMENSIONS.height, - maximumHeight: DEFAULT_EDITOR_MAX_DIMENSIONS.height, - onDidChange: this._onDidViewChange.event, - layout: (width: number, height: number, top: number, left: number) => { - setStyle(this.htmlElements.root, { width, height, top, left }); - this.editor.layout({ - width: width - this.htmlElements.gutterDiv.clientWidth, - height: height - this.htmlElements.title.clientHeight, - }); - } - - // preferredWidth?: number | undefined; - // preferredHeight?: number | undefined; - // priority?: LayoutPriority | undefined; - // snap?: boolean | undefined; - }; - - private readonly _title = new IconLabel(this.htmlElements.title, { supportIcons: true }); - private readonly _detail = new IconLabel(this.htmlElements.title, { supportIcons: true }); - - public readonly editor = this.instantiationService.createInstance( - CodeEditorWidget, - this.htmlElements.editor, - { - minimap: { enabled: false }, - readOnly: this._options.readonly, - glyphMargin: false, - lineNumbersMinChars: 2, - }, - { contributions: [] } - ); - - constructor( - private readonly _options: ICodeEditorViewOptions, - @IInstantiationService - private readonly instantiationService: IInstantiationService - ) { - super(); - } - - public setModel( - model: MergeEditorModel, - textModel: ITextModel, - title: string, - description: string | undefined, - detail: string | undefined - ): void { - this.editor.setModel(textModel); - this._title.setLabel(title, description); - this._detail.setLabel('', detail); - - this._model.set(model, undefined); - } -} - -class InputCodeEditorView extends CodeEditorView { - private readonly decorations = derivedObservable('decorations', reader => { - const model = this.model.read(reader); - if (!model) { - return []; - } - const result = new Array(); - for (const m of model.modifiedBaseRanges.read(reader)) { - const range = m.getInputRange(this.inputNumber); - if (!range.isEmpty) { - result.push({ - range: new Range(range.startLineNumber, 1, range.endLineNumberExclusive - 1, 1), - options: { - isWholeLine: true, - className: `merge-editor-modified-base-range-input${this.inputNumber}`, - description: 'Base Range Projection' - } - }); - - const inputDiffs = m.getInputDiffs(this.inputNumber); - for (const diff of inputDiffs) { - if (diff.rangeMappings) { - for (const d of diff.rangeMappings) { - result.push({ - range: d.outputRange, - options: { - className: `merge-editor-diff-input${this.inputNumber}`, - description: 'Base Range Projection' - } - }); - } - } - } - } - } - return result; - }); - - constructor( - public readonly inputNumber: 1 | 2, - options: ICodeEditorViewOptions, - @IInstantiationService instantiationService: IInstantiationService - ) { - super(options, instantiationService); - - this._register(applyObservableDecorations(this.editor, this.decorations)); - - this._register( - new EditorGutter(this.editor, this.htmlElements.gutterDiv, { - getIntersectingGutterItems: (range, reader) => { - const model = this.model.read(reader); - if (!model) { return []; } - return model.modifiedBaseRanges.read(reader) - .filter((r) => r.getInputDiffs(this.inputNumber).length > 0) - .map((baseRange, idx) => ({ - id: idx.toString(), - additionalHeightInPx: 0, - offsetInPx: 0, - range: baseRange.getInputRange(this.inputNumber), - enabled: model.isUpToDate, - toggleState: derivedObservable('toggle', (reader) => - model - .getState(baseRange) - .read(reader) - .getInput(this.inputNumber) - ), - setState: (value, tx) => - model.setState( - baseRange, - model - .getState(baseRange) - .get() - .withInputValue(this.inputNumber, value), - tx - ), - })); - }, - createView: (item, target) => - new MergeConflictGutterItemView(item, target), - }) - ); - } -} - -interface ModifiedBaseRangeGutterItemInfo extends IGutterItemInfo { - enabled: IObservable; - toggleState: IObservable; - setState(value: boolean, tx: ITransaction): void; -} - -class MergeConflictGutterItemView extends Disposable implements IGutterItemView { - private readonly item = new ObservableValue(undefined, 'item'); - - constructor(item: ModifiedBaseRangeGutterItemInfo, private readonly target: HTMLElement) { - super(); - - this.item.set(item, undefined); - - target.classList.add('merge-accept-gutter-marker'); - - const checkBox = new Toggle({ isChecked: false, title: localize('acceptMerge', "Accept Merge"), icon: Codicon.check }); - checkBox.domNode.classList.add('accept-conflict-group'); - - this._register( - autorun((reader) => { - const item = this.item.read(reader)!; - const value = item.toggleState.read(reader); - const iconMap: Record = { - [InputState.excluded]: { icon: undefined, checked: false }, - [InputState.conflicting]: { icon: Codicon.circleFilled, checked: false }, - [InputState.first]: { icon: Codicon.check, checked: true }, - [InputState.second]: { icon: Codicon.checkAll, checked: true }, - }; - checkBox.setIcon(iconMap[value].icon); - checkBox.checked = iconMap[value].checked; - - if (!item.enabled.read(reader)) { - checkBox.disable(); - } else { - checkBox.enable(); - } - }, 'Update Toggle State') - ); - - this._register(checkBox.onChange(() => { - transaction(tx => { - this.item.get()!.setState(checkBox.checked, tx); - }); - })); - - target.appendChild(n('div.background', [noBreakWhitespace]).root); - target.appendChild( - n('div.checkbox', [n('div.checkbox-background', [checkBox.domNode])]).root - ); - } - - layout(top: number, height: number, viewTop: number, viewHeight: number): void { - this.target.classList.remove('multi-line'); - this.target.classList.remove('single-line'); - this.target.classList.add(height > 30 ? 'multi-line' : 'single-line'); - } - - update(baseRange: ModifiedBaseRangeGutterItemInfo): void { - this.item.set(baseRange, undefined); - } -} - -class ResultCodeEditorView extends CodeEditorView { - private readonly decorations = derivedObservable('decorations', reader => { - const model = this.model.read(reader); - if (!model) { - return []; - } - const result = new Array(); - - const baseRangeWithStoreAndTouchingDiffs = join( - model.modifiedBaseRanges.read(reader), - model.resultDiffs.read(reader), - (baseRange, diff) => - baseRange.baseRange.touches(diff.inputRange) - ? CompareResult.neitherLessOrGreaterThan - : LineRange.compareByStart( - baseRange.baseRange, - diff.inputRange - ) - ); - - for (const m of baseRangeWithStoreAndTouchingDiffs) { - for (const r of m.rights) { - const range = r.outputRange; - - const state = m.left ? model.getState(m.left).read(reader) : undefined; - - if (!range.isEmpty) { - result.push({ - range: new Range(range.startLineNumber, 1, range.endLineNumberExclusive - 1, 1), - options: { - isWholeLine: true, - // TODO - - className: (() => { - if (state) { - if (state.input1 && !state.input2) { - return 'merge-editor-modified-base-range-input1'; - } - if (state.input2 && !state.input1) { - return 'merge-editor-modified-base-range-input2'; - } - if (state.input1 && state.input2) { - return 'merge-editor-modified-base-range-combination'; - } - } - return 'merge-editor-modified-base-range'; - })(), - description: 'Result Diff' - } - }); - } - } - } - return result; - }); - - constructor( - options: ICodeEditorViewOptions, - @IInstantiationService instantiationService: IInstantiationService - ) { - super(options, instantiationService); - - this._register(applyObservableDecorations(this.editor, this.decorations)); - } -} From 37b410c3377ab1a71441f7f37530a3f6177bac47 Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Thu, 16 Jun 2022 10:59:22 -0700 Subject: [PATCH 101/175] Refactor Settings indicators to use custom hovers (#152359) Ref #151787 --- .../settingsEditorSettingIndicators.ts | 97 ++++++++++--------- 1 file changed, 52 insertions(+), 45 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts index fff6d5a1420..9bfac0d0356 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts @@ -5,6 +5,8 @@ import * as DOM from 'vs/base/browser/dom'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { IHoverDelegate, IHoverDelegateOptions } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; import { SimpleIconLabel } from 'vs/base/browser/ui/iconLabel/simpleIconLabel'; import { Emitter } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; @@ -13,6 +15,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { getIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge'; import { getDefaultIgnoredSettings, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; +import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; const $ = DOM.$; @@ -25,26 +28,31 @@ export interface ISettingOverrideClickEvent { * Renders the indicators next to a setting, such as "Also Modified In". */ export class SettingsTreeIndicatorsLabel { - /** - * This element wraps around the other elements. - */ - private labelElement: HTMLElement; + private indicatorsContainerElement: HTMLElement; private scopeOverridesElement: HTMLElement; private syncIgnoredElement: HTMLElement; private defaultOverrideIndicatorElement: HTMLElement; - private defaultOverrideIndicatorLabel: SimpleIconLabel; + private hoverDelegate: IHoverDelegate; constructor( container: HTMLElement, + @IConfigurationService configurationService: IConfigurationService, + @IHoverService hoverService: IHoverService, @IUserDataSyncEnablementService private readonly userDataSyncEnablementService: IUserDataSyncEnablementService) { - this.labelElement = DOM.append(container, $('.misc-label')); - this.labelElement.style.display = 'inline'; + this.indicatorsContainerElement = DOM.append(container, $('.misc-label')); + this.indicatorsContainerElement.style.display = 'inline'; this.scopeOverridesElement = this.createScopeOverridesElement(); this.syncIgnoredElement = this.createSyncIgnoredElement(); - const { element: defaultOverrideElement, label: defaultOverrideLabel } = this.createDefaultOverrideIndicator(); - this.defaultOverrideIndicatorElement = defaultOverrideElement; - this.defaultOverrideIndicatorLabel = defaultOverrideLabel; + this.defaultOverrideIndicatorElement = this.createDefaultOverrideIndicator(); + + this.hoverDelegate = { + showHover: (options: IHoverDelegateOptions, focus?: boolean) => { + return hoverService.showHover(options, focus); + }, + delay: configurationService.getValue('workbench.hover.delay'), + placement: 'element' + }; } private createScopeOverridesElement(): HTMLElement { @@ -55,15 +63,17 @@ export class SettingsTreeIndicatorsLabel { private createSyncIgnoredElement(): HTMLElement { const syncIgnoredElement = $('span.setting-item-ignored'); const syncIgnoredLabel = new SimpleIconLabel(syncIgnoredElement); - syncIgnoredLabel.text = '$(info) ' + localize('extensionSyncIgnoredLabel', 'Setting not synced'); - syncIgnoredLabel.title = localize('syncIgnoredTitle', "Settings sync does not sync this setting"); + syncIgnoredLabel.text = '$(info) ' + localize('extensionSyncIgnoredLabel', 'Not synced'); + const syncIgnoredHoverContent = localize('syncIgnoredTitle', "Settings sync does not sync this setting"); + setupCustomHover(this.hoverDelegate, syncIgnoredElement, syncIgnoredHoverContent); return syncIgnoredElement; } - private createDefaultOverrideIndicator(): { element: HTMLElement; label: SimpleIconLabel } { + private createDefaultOverrideIndicator(): HTMLElement { const defaultOverrideIndicator = $('span.setting-item-default-overridden'); const defaultOverrideLabel = new SimpleIconLabel(defaultOverrideIndicator); - return { element: defaultOverrideIndicator, label: defaultOverrideLabel }; + defaultOverrideLabel.text = '$(info) ' + localize('defaultOverriddenLabel', "Default value changed"); + return defaultOverrideIndicator; } private render() { @@ -71,17 +81,17 @@ export class SettingsTreeIndicatorsLabel { return element.style.display !== 'none'; }); - this.labelElement.innerText = ''; - this.labelElement.style.display = 'none'; + this.indicatorsContainerElement.innerText = ''; + this.indicatorsContainerElement.style.display = 'none'; if (elementsToShow.length) { - this.labelElement.style.display = 'inline'; - DOM.append(this.labelElement, $('span', undefined, '(')); + this.indicatorsContainerElement.style.display = 'inline'; + DOM.append(this.indicatorsContainerElement, $('span', undefined, '(')); for (let i = 0; i < elementsToShow.length - 1; i++) { - DOM.append(this.labelElement, elementsToShow[i]); - DOM.append(this.labelElement, $('span.comma', undefined, ' • ')); + DOM.append(this.indicatorsContainerElement, elementsToShow[i]); + DOM.append(this.indicatorsContainerElement, $('span.comma', undefined, ' • ')); } - DOM.append(this.labelElement, elementsToShow[elementsToShow.length - 1]); - DOM.append(this.labelElement, $('span', undefined, ')')); + DOM.append(this.indicatorsContainerElement, elementsToShow[elementsToShow.length - 1]); + DOM.append(this.indicatorsContainerElement, $('span', undefined, ')')); } } @@ -125,24 +135,29 @@ export class SettingsTreeIndicatorsLabel { updateDefaultOverrideIndicator(element: SettingsTreeSettingElement) { this.defaultOverrideIndicatorElement.style.display = 'none'; - const defaultValueSource = element.defaultValueSource; - if (defaultValueSource) { + const sourceToDisplay = getDefaultValueSourceToDisplay(element); + if (sourceToDisplay !== undefined) { this.defaultOverrideIndicatorElement.style.display = 'inline'; - let sourceToDisplay = ''; - if (typeof defaultValueSource !== 'string' && defaultValueSource.id !== element.setting.extensionInfo?.id) { - sourceToDisplay = defaultValueSource.displayName ?? defaultValueSource.id; - } else if (typeof defaultValueSource === 'string') { - sourceToDisplay = defaultValueSource; - } - if (sourceToDisplay) { - this.defaultOverrideIndicatorLabel.title = localize('defaultOverriddenDetails', "Default setting value overridden by {0}", sourceToDisplay); - this.defaultOverrideIndicatorLabel.text = '$(info) ' + localize('defaultOverriddenLabel', "Default value changed"); - } + const defaultOverrideHoverContent = localize('defaultOverriddenDetails', "Default setting value overridden by {0}", sourceToDisplay); + setupCustomHover(this.hoverDelegate, this.defaultOverrideIndicatorElement, defaultOverrideHoverContent); } this.render(); } } +function getDefaultValueSourceToDisplay(element: SettingsTreeSettingElement): string | undefined { + let sourceToDisplay: string | undefined; + const defaultValueSource = element.defaultValueSource; + if (defaultValueSource) { + if (typeof defaultValueSource !== 'string' && defaultValueSource.id !== element.setting.extensionInfo?.id) { + sourceToDisplay = defaultValueSource.displayName ?? defaultValueSource.id; + } else if (typeof defaultValueSource === 'string') { + sourceToDisplay = defaultValueSource; + } + } + return sourceToDisplay; +} + export function getIndicatorsLabelAriaLabel(element: SettingsTreeSettingElement, configurationService: IConfigurationService): string { const ariaLabelSections: string[] = []; @@ -162,17 +177,9 @@ export function getIndicatorsLabelAriaLabel(element: SettingsTreeSettingElement, } // Add default override indicator text - if (element.defaultValueSource) { - const defaultValueSource = element.defaultValueSource; - let sourceToDisplay = ''; - if (typeof defaultValueSource !== 'string' && defaultValueSource.id !== element.setting.extensionInfo?.id) { - sourceToDisplay = defaultValueSource.displayName ?? defaultValueSource.id; - } else if (typeof defaultValueSource === 'string') { - sourceToDisplay = defaultValueSource; - } - if (sourceToDisplay) { - ariaLabelSections.push(localize('defaultOverriddenDetails', "Default setting value overridden by {0}", sourceToDisplay)); - } + const sourceToDisplay = getDefaultValueSourceToDisplay(element); + if (sourceToDisplay !== undefined) { + ariaLabelSections.push(localize('defaultOverriddenDetails', "Default setting value overridden by {0}", sourceToDisplay)); } const ariaLabel = ariaLabelSections.join('. '); From cb18a4870cb33215bbe21c9feac4d6b1003fa3fc Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 16 Jun 2022 11:03:34 -0700 Subject: [PATCH 102/175] Don't over-escape text edits (#152350) Fixes #152272 --- src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts index 047600a3912..c63d63003c6 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts @@ -134,8 +134,7 @@ class EditorEditTask extends ModelEditTask { } else { this._edits = this._edits - .sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range)) - .map(edit => ({ ...edit, text: edit.text && SnippetParser.escape(edit.text) })); + .sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range)); this._editor.executeEdits('', this._edits); } } From 6c252851f29c69351b6e15148c43fdf1508ba05f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 16 Jun 2022 11:14:01 -0700 Subject: [PATCH 103/175] Revalidate linked files on header change (#152366) Fixes #150945 With this change, when the headers in a file change, we should also try to revalidate all files that link to it --- .../src/extension.ts | 2 +- .../src/languageFeatures/diagnostics.ts | 168 +++++++++++++----- .../src/test/diagnostic.test.ts | 102 ++++++++++- .../src/test/tableOfContentsWatcher.ts | 64 +++++++ 4 files changed, 286 insertions(+), 50 deletions(-) create mode 100644 extensions/markdown-language-features/src/test/tableOfContentsWatcher.ts diff --git a/extensions/markdown-language-features/src/extension.ts b/extensions/markdown-language-features/src/extension.ts index 59d337710a5..5e4e11510e1 100644 --- a/extensions/markdown-language-features/src/extension.ts +++ b/extensions/markdown-language-features/src/extension.ts @@ -78,7 +78,7 @@ function registerMarkdownLanguageFeatures( vscode.languages.registerDefinitionProvider(selector, new MdDefinitionProvider(referencesProvider)), MdPathCompletionProvider.register(selector, engine, linkComputer), registerDocumentLinkProvider(selector, linkComputer), - registerDiagnostics(selector, engine, workspaceContents, linkComputer, commandManager), + registerDiagnostics(selector, engine, workspaceContents, linkComputer, commandManager, referencesProvider), registerDropIntoEditor(selector), registerPasteProvider(selector), registerFindFileReferences(commandManager, referencesProvider), diff --git a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts index 2b8834adaa0..ab992258292 100644 --- a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts @@ -10,13 +10,15 @@ import { CommandManager } from '../commandManager'; import { MarkdownEngine } from '../markdownEngine'; import { TableOfContents } from '../tableOfContents'; import { Delayer } from '../util/async'; +import { noopToken } from '../util/cancellation'; import { Disposable } from '../util/dispose'; import { isMarkdownFile } from '../util/file'; import { Limiter } from '../util/limiter'; import { ResourceMap } from '../util/resourceMap'; +import { MdTableOfContentsWatcher } from '../test/tableOfContentsWatcher'; import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; import { InternalHref, LinkDefinitionSet, MdLink, MdLinkComputer, MdLinkSource } from './documentLinkProvider'; -import { tryFindMdDocumentForLink } from './references'; +import { MdReferencesProvider, tryFindMdDocumentForLink } from './references'; const localize = nls.loadMessageBundle(); @@ -93,19 +95,21 @@ class InflightDiagnosticRequests { private readonly inFlightRequests = new ResourceMap<{ readonly cts: vscode.CancellationTokenSource }>(); - public trigger(resource: vscode.Uri, compute: (token: vscode.CancellationToken) => Promise) { + public async trigger(resource: vscode.Uri, compute: (token: vscode.CancellationToken) => Promise): Promise { this.cancel(resource); const cts = new vscode.CancellationTokenSource(); const entry = { cts }; this.inFlightRequests.set(resource, entry); - compute(cts.token).finally(() => { + try { + return await compute(cts.token); + } finally { if (this.inFlightRequests.get(resource) === entry) { this.inFlightRequests.delete(resource); } cts.dispose(); - }); + } } public cancel(resource: vscode.Uri) { @@ -226,55 +230,136 @@ class LinkDoesNotExistDiagnostic extends vscode.Diagnostic { } } -export class DiagnosticManager extends Disposable { +export abstract class DiagnosticReporter extends Disposable { + private readonly pending = new ResourceMap>(); + + public clear(): void { + this.pending.clear(); + } + + public abstract set(uri: vscode.Uri, diagnostics: readonly vscode.Diagnostic[]): void; + + public delete(uri: vscode.Uri): void { + this.pending.delete(uri); + } + + public signalTriggered(uri: vscode.Uri, recompute: Promise): void { + this.pending.set(uri, recompute); + recompute.finally(() => { + if (this.pending.get(uri) === recompute) { + this.pending.delete(uri); + } + }); + } + + public async waitAllPending(): Promise { + await Promise.all([...this.pending.values()]); + } +} + +export class DiagnosticCollectionReporter extends DiagnosticReporter { private readonly collection: vscode.DiagnosticCollection; + constructor() { + super(); + this.collection = this._register(vscode.languages.createDiagnosticCollection('markdown')); + } + + public override clear(): void { + super.clear(); + this.collection.clear(); + } + + public set(uri: vscode.Uri, diagnostics: readonly vscode.Diagnostic[]): void { + const tabs = this.getAllTabResources(); + this.collection.set(uri, tabs.has(uri) ? diagnostics : []); + } + + public override delete(uri: vscode.Uri): void { + super.delete(uri); + this.collection.delete(uri); + } + + private getAllTabResources(): ResourceMap { + const openedTabDocs = new ResourceMap(); + for (const group of vscode.window.tabGroups.all) { + for (const tab of group.tabs) { + if (tab.input instanceof vscode.TabInputText) { + openedTabDocs.set(tab.input.uri); + } + } + } + return openedTabDocs; + } +} + +export class DiagnosticManager extends Disposable { + private readonly diagnosticDelayer: Delayer; private readonly pendingDiagnostics = new Set(); private readonly inFlightDiagnostics = this._register(new InflightDiagnosticRequests()); private readonly linkWatcher = this._register(new LinkWatcher()); + private readonly tableOfContentsWatcher: MdTableOfContentsWatcher; + + public readonly ready: Promise; constructor( + engine: MarkdownEngine, + private readonly workspaceContents: MdWorkspaceContents, private readonly computer: DiagnosticComputer, private readonly configuration: DiagnosticConfiguration, + private readonly reporter: DiagnosticReporter, + private readonly referencesProvider: MdReferencesProvider, + delay = 300, ) { super(); - this.diagnosticDelayer = this._register(new Delayer(300)); - - this.collection = this._register(vscode.languages.createDiagnosticCollection('markdown')); + this.diagnosticDelayer = this._register(new Delayer(delay)); this._register(this.configuration.onDidChange(() => { this.rebuild(); })); - this._register(vscode.workspace.onDidOpenTextDocument(doc => { - this.triggerDiagnostics(doc); + this._register(workspaceContents.onDidCreateMarkdownDocument(doc => { + this.triggerDiagnostics(doc.uri); })); - this._register(vscode.workspace.onDidChangeTextDocument(e => { - this.triggerDiagnostics(e.document); + this._register(workspaceContents.onDidChangeMarkdownDocument(doc => { + this.triggerDiagnostics(doc.uri); })); this._register(vscode.workspace.onDidCloseTextDocument(({ uri }) => { this.pendingDiagnostics.delete(uri); this.inFlightDiagnostics.cancel(uri); this.linkWatcher.deleteDocument(uri); - this.collection.delete(uri); + this.reporter.delete(uri); })); this._register(this.linkWatcher.onDidChangeLinkedToFile(changedDocuments => { for (const resource of changedDocuments) { const doc = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === resource.toString()); - if (doc) { - this.triggerDiagnostics(doc); + if (doc && isMarkdownFile(doc)) { + this.triggerDiagnostics(doc.uri); } } })); - this.rebuild(); + this.tableOfContentsWatcher = this._register(new MdTableOfContentsWatcher(engine, workspaceContents)); + this._register(this.tableOfContentsWatcher.onTocChanged(async e => { + // When the toc of a document changes, revalidate every file that linked to it too + const triggered = new ResourceMap(); + for (const ref of await this.referencesProvider.getAllReferencesToFile(e.uri, noopToken)) { + const file = ref.location.uri; + if (!triggered.has(file)) { + this.triggerDiagnostics(file); + triggered.set(file); + } + } + })); + + this.ready = this.rebuild(); } public override dispose() { @@ -294,49 +379,33 @@ export class DiagnosticManager extends Disposable { const pending = [...this.pendingDiagnostics]; this.pendingDiagnostics.clear(); - for (const resource of pending) { - const doc = vscode.workspace.textDocuments.find(doc => doc.uri.fsPath === resource.fsPath); + await Promise.all(pending.map(async resource => { + const doc = await this.workspaceContents.getMarkdownDocument(resource); if (doc) { - this.inFlightDiagnostics.trigger(doc.uri, async (token) => { + await this.inFlightDiagnostics.trigger(doc.uri, async (token) => { const state = await this.recomputeDiagnosticState(doc, token); this.linkWatcher.updateLinksForDocument(doc.uri, state.config.enabled && state.config.validateFileLinks ? state.links : []); - this.collection.set(doc.uri, state.diagnostics); + this.reporter.set(doc.uri, state.diagnostics); }); } - } + })); } private async rebuild() { - this.collection.clear(); + this.reporter.clear(); this.pendingDiagnostics.clear(); this.inFlightDiagnostics.clear(); - const allOpenedTabResources = this.getAllTabResources(); - await Promise.all( - vscode.workspace.textDocuments - .filter(doc => allOpenedTabResources.has(doc.uri) && isMarkdownFile(doc)) - .map(doc => this.triggerDiagnostics(doc))); + for (const doc of await this.workspaceContents.getAllMarkdownDocuments()) { + this.triggerDiagnostics(doc.uri); + } } - private getAllTabResources(): ResourceMap { - const openedTabDocs = new ResourceMap(); - for (const group of vscode.window.tabGroups.all) { - for (const tab of group.tabs) { - if (tab.input instanceof vscode.TabInputText) { - openedTabDocs.set(tab.input.uri); - } - } - } - return openedTabDocs; - } + private triggerDiagnostics(uri: vscode.Uri) { + this.inFlightDiagnostics.cancel(uri); - private triggerDiagnostics(doc: vscode.TextDocument) { - this.inFlightDiagnostics.cancel(doc.uri); - - if (isMarkdownFile(doc)) { - this.pendingDiagnostics.add(doc.uri); - this.diagnosticDelayer.trigger(() => this.recomputePendingDiagnostics()); - } + this.pendingDiagnostics.add(uri); + this.reporter.signalTriggered(uri, this.diagnosticDelayer.trigger(() => this.recomputePendingDiagnostics())); } } @@ -558,9 +627,16 @@ export function register( workspaceContents: MdWorkspaceContents, linkComputer: MdLinkComputer, commandManager: CommandManager, + referenceProvider: MdReferencesProvider, ): vscode.Disposable { const configuration = new VSCodeDiagnosticConfiguration(); - const manager = new DiagnosticManager(new DiagnosticComputer(engine, workspaceContents, linkComputer), configuration); + const manager = new DiagnosticManager( + engine, + workspaceContents, + new DiagnosticComputer(engine, workspaceContents, linkComputer), + configuration, + new DiagnosticCollectionReporter(), + referenceProvider); return vscode.Disposable.from( configuration, manager, diff --git a/extensions/markdown-language-features/src/test/diagnostic.test.ts b/extensions/markdown-language-features/src/test/diagnostic.test.ts index fe880e5bbd8..caf88bac3e6 100644 --- a/extensions/markdown-language-features/src/test/diagnostic.test.ts +++ b/extensions/markdown-language-features/src/test/diagnostic.test.ts @@ -6,10 +6,13 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; -import { DiagnosticComputer, DiagnosticConfiguration, DiagnosticLevel, DiagnosticManager, DiagnosticOptions } from '../languageFeatures/diagnostics'; +import { DiagnosticCollectionReporter, DiagnosticComputer, DiagnosticConfiguration, DiagnosticLevel, DiagnosticManager, DiagnosticOptions, DiagnosticReporter } from '../languageFeatures/diagnostics'; import { MdLinkComputer } from '../languageFeatures/documentLinkProvider'; +import { MdReferencesProvider } from '../languageFeatures/references'; +import { githubSlugifier } from '../slugify'; import { noopToken } from '../util/cancellation'; import { InMemoryDocument } from '../util/inMemoryDocument'; +import { ResourceMap } from '../util/resourceMap'; import { MdWorkspaceContents } from '../workspaceContents'; import { createNewMarkdownEngine } from './engine'; import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; @@ -32,10 +35,22 @@ async function getComputedDiagnostics(doc: InMemoryDocument, workspaceContents: ).diagnostics; } -function createDiagnosticsManager(workspaceContents: MdWorkspaceContents, configuration = new MemoryDiagnosticConfiguration({})) { +function createDiagnosticsManager( + workspaceContents: MdWorkspaceContents, + configuration = new MemoryDiagnosticConfiguration({}), + reporter: DiagnosticReporter = new DiagnosticCollectionReporter(), +) { const engine = createNewMarkdownEngine(); const linkComputer = new MdLinkComputer(engine); - return new DiagnosticManager(new DiagnosticComputer(engine, workspaceContents, linkComputer), configuration); + const referencesProvider = new MdReferencesProvider(linkComputer, workspaceContents, engine, githubSlugifier); + return new DiagnosticManager( + engine, + workspaceContents, + new DiagnosticComputer(engine, workspaceContents, linkComputer), + configuration, + reporter, + referencesProvider, + 0); } function assertDiagnosticsEqual(actual: readonly vscode.Diagnostic[], expectedRanges: readonly vscode.Range[]) { @@ -72,6 +87,29 @@ class MemoryDiagnosticConfiguration implements DiagnosticConfiguration { } } +class MemoryDiagnosticReporter extends DiagnosticReporter { + public readonly diagnostics = new ResourceMap(); + + override dispose(): void { + super.clear(); + this.clear(); + } + + override clear(): void { + super.clear(); + this.diagnostics.clear(); + } + + set(uri: vscode.Uri, diagnostics: readonly vscode.Diagnostic[]): void { + this.diagnostics.set(uri, diagnostics); + } + + override delete(uri: vscode.Uri): void { + super.delete(uri); + this.diagnostics.delete(uri); + } +} + suite('markdown: Diagnostics', () => { test('Should not return any diagnostics for empty document', async () => { @@ -387,6 +425,64 @@ suite('markdown: Diagnostics', () => { new vscode.Range(3, 14, 3, 22), ]); }); + + test('Should revalidate linked files when header changes', async () => { + const doc1Uri = workspacePath('doc1.md'); + const doc1 = new InMemoryDocument(doc1Uri, joinLines( + `[text](#no-such)`, + `[text](/doc2.md#header)`, + )); + const doc2Uri = workspacePath('doc2.md'); + const doc2 = new InMemoryDocument(doc2Uri, joinLines( + `# Header`, + `[text](#header)`, + `[text](#no-such-2)`, + )); + + const contents = new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]); + const reporter = new MemoryDiagnosticReporter(); + + const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration({}), reporter); + await manager.ready; + + // Check initial state + await reporter.waitAllPending(); + assertDiagnosticsEqual(reporter.diagnostics.get(doc1Uri)!, [ + new vscode.Range(0, 7, 0, 15), + ]); + assertDiagnosticsEqual(reporter.diagnostics.get(doc2Uri)!, [ + new vscode.Range(2, 7, 2, 17), + ]); + + // Edit header + contents.updateDocument(new InMemoryDocument(doc2Uri, joinLines( + `# new header`, + `[text](#new-header)`, + `[text](#no-such-2)`, + ))); + await reporter.waitAllPending(); + assertDiagnosticsEqual(orderDiagnosticsByRange(reporter.diagnostics.get(doc1Uri)!), [ + new vscode.Range(0, 7, 0, 15), + new vscode.Range(1, 15, 1, 22), + ]); + assertDiagnosticsEqual(reporter.diagnostics.get(doc2Uri)!, [ + new vscode.Range(2, 7, 2, 17), + ]); + + // Revert to original file + contents.updateDocument(new InMemoryDocument(doc2Uri, joinLines( + `# header`, + `[text](#header)`, + `[text](#no-such-2)`, + ))); + await reporter.waitAllPending(); + assertDiagnosticsEqual(orderDiagnosticsByRange(reporter.diagnostics.get(doc1Uri)!), [ + new vscode.Range(0, 7, 0, 15) + ]); + assertDiagnosticsEqual(reporter.diagnostics.get(doc2Uri)!, [ + new vscode.Range(2, 7, 2, 17), + ]); + }); }); function orderDiagnosticsByRange(diagnostics: Iterable): readonly vscode.Diagnostic[] { diff --git a/extensions/markdown-language-features/src/test/tableOfContentsWatcher.ts b/extensions/markdown-language-features/src/test/tableOfContentsWatcher.ts new file mode 100644 index 00000000000..cef3c33816f --- /dev/null +++ b/extensions/markdown-language-features/src/test/tableOfContentsWatcher.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { MarkdownEngine } from '../markdownEngine'; +import { TableOfContents } from '../tableOfContents'; +import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; +import { equals } from '../util/arrays'; +import { Disposable } from '../util/dispose'; +import { ResourceMap } from '../util/resourceMap'; + +/** + * Check if the items in a table of contents have changed. + * + * This only checks for changes in the entries themselves, not for any changes in their locations. + */ +function hasTableOfContentsChanged(a: TableOfContents, b: TableOfContents): boolean { + const aSlugs = a.entries.map(entry => entry.slug.value).sort(); + const bSlugs = b.entries.map(entry => entry.slug.value).sort(); + return !equals(aSlugs, bSlugs); +} + +export class MdTableOfContentsWatcher extends Disposable { + + private readonly _files = new ResourceMap<{ + readonly toc: TableOfContents; + }>(); + + private readonly _onTocChanged = this._register(new vscode.EventEmitter<{ readonly uri: vscode.Uri }>); + public readonly onTocChanged = this._onTocChanged.event; + + public constructor( + private readonly engine: MarkdownEngine, + private readonly workspaceContents: MdWorkspaceContents, + ) { + super(); + + this._register(this.workspaceContents.onDidChangeMarkdownDocument(this.onDidChangeDocument, this)); + this._register(this.workspaceContents.onDidCreateMarkdownDocument(this.onDidCreateDocument, this)); + this._register(this.workspaceContents.onDidDeleteMarkdownDocument(this.onDidDeleteDocument, this)); + } + + private async onDidCreateDocument(document: SkinnyTextDocument) { + const toc = await TableOfContents.create(this.engine, document); + this._files.set(document.uri, { toc }); + } + + private async onDidChangeDocument(document: SkinnyTextDocument) { + const existing = this._files.get(document.uri); + const newToc = await TableOfContents.create(this.engine, document); + + if (!existing || hasTableOfContentsChanged(existing.toc, newToc)) { + this._onTocChanged.fire({ uri: document.uri }); + } + + this._files.set(document.uri, { toc: newToc }); + } + + private onDidDeleteDocument(resource: vscode.Uri) { + this._files.delete(resource); + } +} From 48809de3f9e7e028cbf90f51516a547d8f7c3970 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Thu, 16 Jun 2022 14:18:00 -0400 Subject: [PATCH 104/175] Update module ignores to include AI shims required by 1DS (#152367) --- build/.moduleignore | 1 + build/.webignore | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/build/.moduleignore b/build/.moduleignore index a2e1b715e1b..73c3f623db8 100644 --- a/build/.moduleignore +++ b/build/.moduleignore @@ -124,6 +124,7 @@ prebuild-install/**/* @microsoft/applicationinsights*/** @microsoft/dynamicproto-js/** +!@microsoft/applicationinsights-shims/dist/** !@microsoft/applicationinsights-web/dist/applicationinsights-web.min.js # other node modules diff --git a/build/.webignore b/build/.webignore index f55882ba9d4..3aaafbceb21 100644 --- a/build/.webignore +++ b/build/.webignore @@ -35,4 +35,5 @@ xterm-addon-webgl/out/** @microsoft/applicationinsights*/** @microsoft/dynamicproto-js/** -!@microsoft/applicationinsights-web/dist/applicationinsights-web.min.js \ No newline at end of file +!@microsoft/applicationinsights-shims/dist/** +!@microsoft/applicationinsights-web/dist/applicationinsights-web.min.js From c3c62cb0542ecaee61e34cbd8d11bfef71df73b6 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 16 Jun 2022 20:22:19 +0200 Subject: [PATCH 105/175] Fixes scroll synchronization bug. --- .../mergeEditor/browser/model/mapping.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/mapping.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/mapping.ts index 6cb52ce450e..cfbd5cc48ee 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/mapping.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/mapping.ts @@ -162,15 +162,17 @@ export class DocumentMapping { if (lastBefore.getRange(containingDirection).contains(lineNumber)) { return lastBefore; } - return new LineRangeMapping( - new LineRange(lineNumber, 1), - new LineRange( - lineNumber + - lastBefore.getRange(mapTo).endLineNumberExclusive - - lastBefore.getRange(containingDirection).endLineNumberExclusive, - 1 - ) + const containingRange = new LineRange(lineNumber, 1); + const mappedRange = new LineRange( + lineNumber + + lastBefore.getRange(mapTo).endLineNumberExclusive - + lastBefore.getRange(containingDirection).endLineNumberExclusive, + 1 ); + + return containingDirection === MappingDirection.input + ? new LineRangeMapping(containingRange, mappedRange) + : new LineRangeMapping(mappedRange, containingRange); } return new LineRangeMapping( new LineRange(lineNumber, 1), From e8f28ac3ac798f93edf9c3e1f379cdfff3303732 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Thu, 16 Jun 2022 14:26:06 -0400 Subject: [PATCH 106/175] Refactor location of web app insights appender (#152358) * Refactor location of web app insights appender * Play around with eslintrc * Eslint json update * Try and fix monaco editor check * Remove extra trailing comma --- .eslintrc.json | 7 +- src/tsconfig.monaco.json | 1 + .../telemetry/browser/appInsightsAppender.ts | 77 ++++++++++++++++ .../telemetry/browser/telemetryService.ts | 89 +------------------ 4 files changed, 84 insertions(+), 90 deletions(-) create mode 100644 src/vs/platform/telemetry/browser/appInsightsAppender.ts diff --git a/.eslintrc.json b/.eslintrc.json index fa9e0d1ba89..a02921b9f6b 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -210,7 +210,8 @@ // - electron-browser "when": "hasBrowser", "allow": [ - "vs/css!./**/*" + "vs/css!./**/*", + "@microsoft/applicationinsights-web" ] }, { @@ -220,7 +221,6 @@ // - electron-main "when": "hasNode", "allow": [ - "@microsoft/applicationinsights-web", "@parcel/watcher", "@vscode/sqlite3", "@vscode/vscode-languagedetection", @@ -445,8 +445,7 @@ }, // TODO@layers "tas-client-umd", // node module allowed even in /common/ "vscode-textmate", // node module allowed even in /common/ - "@vscode/vscode-languagedetection", // node module allowed even in /common/ - "@microsoft/applicationinsights-web" // node module allowed even in /common/ + "@vscode/vscode-languagedetection" // node module allowed even in /common/ ] }, { diff --git a/src/tsconfig.monaco.json b/src/tsconfig.monaco.json index 628e877dcd4..3d942854f5d 100644 --- a/src/tsconfig.monaco.json +++ b/src/tsconfig.monaco.json @@ -30,6 +30,7 @@ "node_modules/*", "vs/platform/files/browser/htmlFileSystemProvider.ts", "vs/platform/files/browser/webFileSystemAccess.ts", + "vs/platform/telemetry/browser/*", "vs/platform/assignment/*" ] } diff --git a/src/vs/platform/telemetry/browser/appInsightsAppender.ts b/src/vs/platform/telemetry/browser/appInsightsAppender.ts new file mode 100644 index 00000000000..2b3f099cf83 --- /dev/null +++ b/src/vs/platform/telemetry/browser/appInsightsAppender.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 type { ApplicationInsights } from '@microsoft/applicationinsights-web'; +import { ITelemetryAppender, validateTelemetryData } from 'vs/platform/telemetry/common/telemetryUtils'; + +export class WebAppInsightsAppender implements ITelemetryAppender { + private _aiClient: ApplicationInsights | undefined; + private _aiClientLoaded = false; + private _telemetryCache: { eventName: string; data: any }[] = []; + + constructor(private _eventPrefix: string, aiKey: string) { + const endpointUrl = 'https://mobile.events.data.microsoft.com/collect/v1'; + import('@microsoft/applicationinsights-web').then(aiLibrary => { + this._aiClient = new aiLibrary.ApplicationInsights({ + config: { + instrumentationKey: aiKey, + endpointUrl, + disableAjaxTracking: true, + disableExceptionTracking: true, + disableFetchTracking: true, + disableCorrelationHeaders: true, + disableCookiesUsage: true, + autoTrackPageVisitTime: false, + emitLineDelimitedJson: true, + }, + }); + this._aiClient.loadAppInsights(); + // Client is loaded we can now flush the cached events + this._aiClientLoaded = true; + this._telemetryCache.forEach(cacheEntry => this.log(cacheEntry.eventName, cacheEntry.data)); + this._telemetryCache = []; + + // If we cannot access the endpoint this most likely means it's being blocked + // and we should not attempt to send any telemetry. + fetch(endpointUrl, { method: 'POST' }).catch(() => (this._aiClient = undefined)); + }).catch(err => { + console.error(err); + }); + } + + /** + * Logs a telemetry event with eventName and data + * @param eventName The event name + * @param data The data associated with the events + */ + public log(eventName: string, data: any): void { + if (!this._aiClient && this._aiClientLoaded) { + return; + } else if (!this._aiClient && !this._aiClientLoaded) { + this._telemetryCache.push({ eventName, data }); + return; + } + + data = validateTelemetryData(data); + + // Web does not expect properties and measurements so we must + // spread them out. This is different from desktop which expects them + data = { ...data.properties, ...data.measurements }; + + // undefined assertion is ok since above two if statements cover both cases + this._aiClient!.trackEvent({ name: this._eventPrefix + '/' + eventName }, data); + } + + /** + * Flushes all the telemetry data still in the buffer + */ + public flush(): Promise { + if (this._aiClient) { + this._aiClient.flush(); + this._aiClient = undefined; + } + return Promise.resolve(undefined); + } +} diff --git a/src/vs/workbench/services/telemetry/browser/telemetryService.ts b/src/vs/workbench/services/telemetry/browser/telemetryService.ts index d720696d8cb..bd161f54f33 100644 --- a/src/vs/workbench/services/telemetry/browser/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/browser/telemetryService.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import type { ApplicationInsights } from '@microsoft/applicationinsights-web'; import { Disposable } from 'vs/base/common/lifecycle'; import { IObservableValue } from 'vs/base/common/observableValue'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -11,98 +10,16 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILoggerService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; import { IStorageService } from 'vs/platform/storage/common/storage'; +import { WebAppInsightsAppender } from 'vs/platform/telemetry/browser/appInsightsAppender'; import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings'; import { ITelemetryData, ITelemetryInfo, ITelemetryService, TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; import { TelemetryLogAppender } from 'vs/platform/telemetry/common/telemetryLogAppender'; import { ITelemetryServiceConfig, TelemetryService as BaseTelemetryService } from 'vs/platform/telemetry/common/telemetryService'; -import { ITelemetryAppender, NullTelemetryService, supportsTelemetry, validateTelemetryData } from 'vs/platform/telemetry/common/telemetryUtils'; +import { ITelemetryAppender, NullTelemetryService, supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { resolveWorkbenchCommonProperties } from 'vs/workbench/services/telemetry/browser/workbenchCommonProperties'; -class WebAppInsightsAppender implements ITelemetryAppender { - private _aiClient: ApplicationInsights | undefined; - private _aiClientLoaded = false; - private _telemetryCache: { eventName: string; data: any }[] = []; - - constructor(private _eventPrefix: string, aiKey: string) { - const endpointUrl = 'https://mobile.events.data.microsoft.com/collect/v1'; - import('@microsoft/applicationinsights-web').then(aiLibrary => { - this._aiClient = new aiLibrary.ApplicationInsights({ - config: { - instrumentationKey: aiKey, - endpointUrl, - disableAjaxTracking: true, - disableExceptionTracking: true, - disableFetchTracking: true, - disableCorrelationHeaders: true, - disableCookiesUsage: true, - autoTrackPageVisitTime: false, - emitLineDelimitedJson: true, - }, - }); - this._aiClient.loadAppInsights(); - // Client is loaded we can now flush the cached events - this._aiClientLoaded = true; - this._telemetryCache.forEach(cacheEntry => this.log(cacheEntry.eventName, cacheEntry.data)); - this._telemetryCache = []; - - // If we cannot access the endpoint this most likely means it's being blocked - // and we should not attempt to send any telemetry. - fetch(endpointUrl, { method: 'POST' }).catch(() => (this._aiClient = undefined)); - }).catch(err => { - console.error(err); - }); - } - - /** - * Logs a telemetry event with eventName and data - * @param eventName The event name - * @param data The data associated with the events - */ - public log(eventName: string, data: any): void { - if (!this._aiClient && this._aiClientLoaded) { - return; - } else if (!this._aiClient && !this._aiClientLoaded) { - this._telemetryCache.push({ eventName, data }); - return; - } - - data = validateTelemetryData(data); - - // Web does not expect properties and measurements so we must - // spread them out. This is different from desktop which expects them - data = { ...data.properties, ...data.measurements }; - - // undefined assertion is ok since above two if statements cover both cases - this._aiClient!.trackEvent({ name: this._eventPrefix + '/' + eventName }, data); - } - - /** - * Flushes all the telemetry data still in the buffer - */ - public flush(): Promise { - if (this._aiClient) { - this._aiClient.flush(); - this._aiClient = undefined; - } - return Promise.resolve(undefined); - } -} - -class WebTelemetryAppender implements ITelemetryAppender { - - constructor(private _appender: ITelemetryAppender) { } - - log(eventName: string, data: any): void { - this._appender.log(eventName, data); - } - - flush(): Promise { - return this._appender.flush(); - } -} - export class TelemetryService extends Disposable implements ITelemetryService { declare readonly _serviceBrand: undefined; @@ -124,7 +41,7 @@ export class TelemetryService extends Disposable implements ITelemetryService { // If remote server is present send telemetry through that, else use the client side appender const telemetryProvider: ITelemetryAppender = remoteAgentService.getConnection() !== null ? { log: remoteAgentService.logTelemetry.bind(remoteAgentService), flush: remoteAgentService.flushTelemetry.bind(remoteAgentService) } : new WebAppInsightsAppender('monacoworkbench', productService.aiConfig?.asimovKey); const config: ITelemetryServiceConfig = { - appenders: [new WebTelemetryAppender(telemetryProvider), new TelemetryLogAppender(loggerService, environmentService)], + appenders: [telemetryProvider, new TelemetryLogAppender(loggerService, environmentService)], commonProperties: resolveWorkbenchCommonProperties(storageService, productService.commit, productService.version, environmentService.remoteAuthority, productService.embedderIdentifier, productService.removeTelemetryMachineId, environmentService.options && environmentService.options.resolveCommonTelemetryProperties), sendErrorTelemetry: this.sendErrorTelemetry, }; From 751c3a72a3f67acdf31fc21efc9b71dd9d261d66 Mon Sep 17 00:00:00 2001 From: andreamah Date: Thu, 16 Jun 2022 12:21:45 -0700 Subject: [PATCH 107/175] shortcuts and command to navigate debug consoles #151772 --- .../debug/browser/debug.contribution.ts | 4 +- .../contrib/debug/browser/debugCommands.ts | 53 ++++++++++++++++++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 95d616fe67b..966e52b75ae 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -20,7 +20,7 @@ import { } from 'vs/workbench/contrib/debug/common/debug'; import { DebugToolBar } from 'vs/workbench/contrib/debug/browser/debugToolBar'; import { DebugService } from 'vs/workbench/contrib/debug/browser/debugService'; -import { ADD_CONFIGURATION_ID, TOGGLE_INLINE_BREAKPOINT_ID, COPY_STACK_TRACE_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, DEBUG_START_LABEL, DEBUG_START_COMMAND_ID, DEBUG_RUN_LABEL, DEBUG_RUN_COMMAND_ID, EDIT_EXPRESSION_COMMAND_ID, REMOVE_EXPRESSION_COMMAND_ID, SELECT_AND_START_ID, SELECT_AND_START_LABEL, SET_EXPRESSION_COMMAND_ID, DISCONNECT_AND_SUSPEND_ID, DISCONNECT_AND_SUSPEND_LABEL } from 'vs/workbench/contrib/debug/browser/debugCommands'; +import { ADD_CONFIGURATION_ID, TOGGLE_INLINE_BREAKPOINT_ID, COPY_STACK_TRACE_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, DEBUG_START_LABEL, DEBUG_START_COMMAND_ID, DEBUG_RUN_LABEL, DEBUG_RUN_COMMAND_ID, EDIT_EXPRESSION_COMMAND_ID, REMOVE_EXPRESSION_COMMAND_ID, SELECT_AND_START_ID, SELECT_AND_START_LABEL, SET_EXPRESSION_COMMAND_ID, DISCONNECT_AND_SUSPEND_ID, DISCONNECT_AND_SUSPEND_LABEL, NEXT_DEBUG_CONSOLE_ID, NEXT_DEBUG_CONSOLE_LABEL, PREV_DEBUG_CONSOLE_ID, PREV_DEBUG_CONSOLE_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'; import { isMacintosh, isWeb } from 'vs/base/common/platform'; @@ -120,6 +120,8 @@ registerDebugCommandPaletteItem(TOGGLE_INLINE_BREAKPOINT_ID, nls.localize('inlin registerDebugCommandPaletteItem(DEBUG_START_COMMAND_ID, DEBUG_START_LABEL, ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE.notEqualsTo(getStateLabel(State.Initializing)))); registerDebugCommandPaletteItem(DEBUG_RUN_COMMAND_ID, DEBUG_RUN_LABEL, ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE.notEqualsTo(getStateLabel(State.Initializing)))); registerDebugCommandPaletteItem(SELECT_AND_START_ID, SELECT_AND_START_LABEL, ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE.notEqualsTo(getStateLabel(State.Initializing)))); +registerDebugCommandPaletteItem(NEXT_DEBUG_CONSOLE_ID, NEXT_DEBUG_CONSOLE_LABEL, CONTEXT_IN_DEBUG_MODE); +registerDebugCommandPaletteItem(PREV_DEBUG_CONSOLE_ID, PREV_DEBUG_CONSOLE_LABEL, CONTEXT_IN_DEBUG_MODE); // Debug callstack context menu diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index 3042792f8e8..78e6c595726 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -8,7 +8,7 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { List } from 'vs/base/browser/ui/list/listWidget'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IListService } from 'vs/platform/list/browser/listService'; -import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_EXPRESSION_SELECTED, IConfig, IStackFrame, IThread, IDebugSession, CONTEXT_DEBUG_STATE, IDebugConfiguration, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, REPL_VIEW_ID, CONTEXT_DEBUGGERS_AVAILABLE, State, getStateLabel, CONTEXT_BREAKPOINT_INPUT_FOCUSED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, VIEWLET_ID, CONTEXT_DISASSEMBLY_VIEW_FOCUS } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_EXPRESSION_SELECTED, IConfig, IStackFrame, IThread, IDebugSession, CONTEXT_DEBUG_STATE, IDebugConfiguration, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, REPL_VIEW_ID, CONTEXT_DEBUGGERS_AVAILABLE, State, getStateLabel, CONTEXT_BREAKPOINT_INPUT_FOCUSED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, VIEWLET_ID, CONTEXT_DISASSEMBLY_VIEW_FOCUS, CONTEXT_IN_DEBUG_REPL } from 'vs/workbench/contrib/debug/common/debug'; import { Expression, Variable, Breakpoint, FunctionBreakpoint, DataBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; import { IExtensionsViewPaneContainer, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -58,6 +58,8 @@ export const DEBUG_RUN_COMMAND_ID = 'workbench.action.debug.run'; export const EDIT_EXPRESSION_COMMAND_ID = 'debug.renameWatchExpression'; export const SET_EXPRESSION_COMMAND_ID = 'debug.setWatchExpression'; export const REMOVE_EXPRESSION_COMMAND_ID = 'debug.removeWatchExpression'; +export const NEXT_DEBUG_CONSOLE_ID = 'workbench.action.debug.nextConsole'; +export const PREV_DEBUG_CONSOLE_ID = 'workbench.action.debug.prevConsole'; export const RESTART_LABEL = nls.localize('restartDebug', "Restart"); export const STEP_OVER_LABEL = nls.localize('stepOverDebug', "Step Over"); @@ -73,6 +75,8 @@ export const SELECT_AND_START_LABEL = nls.localize('selectAndStartDebugging', "S export const DEBUG_CONFIGURE_LABEL = nls.localize('openLaunchJson', "Open '{0}'", 'launch.json'); export const DEBUG_START_LABEL = nls.localize('startDebug', "Start Debugging"); export const DEBUG_RUN_LABEL = nls.localize('startWithoutDebugging', "Start Without Debugging"); +export const NEXT_DEBUG_CONSOLE_LABEL = nls.localize('nextDebugConsole', "Next Debug Console"); +export const PREV_DEBUG_CONSOLE_LABEL = nls.localize('prevDebugConsole', "Previous Debug Console"); interface CallStackContext { sessionId: string; @@ -136,6 +140,33 @@ function isSessionContext(obj: any): obj is CallStackContext { return obj && typeof obj.sessionId === 'string'; } +async function changeDebugConsoleFocus(accessor: ServicesAccessor, next: boolean) { + const debugService = accessor.get(IDebugService); + const sessions = debugService.getModel().getSessions(true).filter(s => s.hasSeparateRepl()); + let currSession = debugService.getViewModel().focusedSession; + + let nextIndex = 0; + if (sessions.length > 0 && currSession) { + while (currSession && !currSession.hasSeparateRepl()) { + currSession = currSession.parentSession; + } + + if (currSession) { + const currIndex = sessions.indexOf(currSession); + if (next) { + nextIndex = (currIndex === (sessions.length - 1) ? 0 : (currIndex + 1)); + } else { + nextIndex = (currIndex === 0 ? (sessions.length - 1) : (currIndex - 1)); + } + } + } + await debugService.focusStackFrame(undefined, undefined, sessions[nextIndex], { explicit: true }); + + const viewsService = accessor.get(IViewsService); + if (!viewsService.isViewVisible(REPL_VIEW_ID)) { + await viewsService.openView(REPL_VIEW_ID, true); + } +} // These commands are used in call stack context menu, call stack inline actions, command palette, debug toolbar, mac native touch bar // When the command is exectued in the context of a thread(context menu on a thread, inline call stack action) we pass the thread id @@ -229,6 +260,26 @@ MenuRegistry.appendMenuItem(MenuId.EditorContext, { order: 3 }); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: NEXT_DEBUG_CONSOLE_ID, + weight: KeybindingWeight.WorkbenchContrib, + when: CONTEXT_IN_DEBUG_REPL, + primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.BracketRight, + handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { + changeDebugConsoleFocus(accessor, true); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: PREV_DEBUG_CONSOLE_ID, + weight: KeybindingWeight.WorkbenchContrib, + when: CONTEXT_IN_DEBUG_REPL, + primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.BracketLeft, + handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { + changeDebugConsoleFocus(accessor, false); + } +}); + KeybindingsRegistry.registerCommandAndKeybindingRule({ id: RESTART_SESSION_ID, weight: KeybindingWeight.WorkbenchContrib, From 3114ee690b7a90da965504f38e9c5f0c14f7eaa8 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 16 Jun 2022 12:47:48 -0700 Subject: [PATCH 108/175] Split out VS Code reference provider from markdown reference provider (#152369) This change renames the main markdown reference provider class to `MdReferenceComputer` and then uses this to implement a `vscode.ReferenceProvider` This more cleanly splits the VS Code part of the logic from the general reference calculation stuff other providers consume --- .../src/extension.ts | 14 ++--- .../languageFeatures/definitionProvider.ts | 8 +-- .../src/languageFeatures/diagnostics.ts | 10 ++-- .../src/languageFeatures/fileReferences.ts | 8 +-- .../src/languageFeatures/references.ts | 51 ++++++++++++------- .../src/languageFeatures/rename.ts | 6 +-- .../src/test/definitionProvider.test.ts | 6 +-- .../src/test/diagnostic.test.ts | 6 +-- .../src/test/fileReferences.test.ts | 6 +-- .../src/test/references.test.ts | 5 +- .../src/test/rename.test.ts | 8 +-- 11 files changed, 73 insertions(+), 55 deletions(-) diff --git a/extensions/markdown-language-features/src/extension.ts b/extensions/markdown-language-features/src/extension.ts index 5e4e11510e1..97fd770e64f 100644 --- a/extensions/markdown-language-features/src/extension.ts +++ b/extensions/markdown-language-features/src/extension.ts @@ -15,7 +15,7 @@ import { registerDropIntoEditor } from './languageFeatures/dropIntoEditor'; import { registerFindFileReferences } from './languageFeatures/fileReferences'; import { MdFoldingProvider } from './languageFeatures/foldingProvider'; import { MdPathCompletionProvider } from './languageFeatures/pathCompletions'; -import { MdReferencesProvider } from './languageFeatures/references'; +import { MdReferencesComputer, registerReferencesProvider } from './languageFeatures/references'; import { MdRenameProvider } from './languageFeatures/rename'; import { MdSmartSelect } from './languageFeatures/smartSelect'; import { MdWorkspaceSymbolProvider } from './languageFeatures/workspaceSymbolProvider'; @@ -66,22 +66,22 @@ function registerMarkdownLanguageFeatures( const linkComputer = new MdLinkComputer(engine); const workspaceContents = new VsCodeMdWorkspaceContents(); - const referencesProvider = new MdReferencesProvider(linkComputer, workspaceContents, engine, githubSlugifier); + const referencesComputer = new MdReferencesComputer(linkComputer, workspaceContents, engine, githubSlugifier); return vscode.Disposable.from( workspaceContents, vscode.languages.registerDocumentSymbolProvider(selector, symbolProvider), vscode.languages.registerFoldingRangeProvider(selector, new MdFoldingProvider(engine)), vscode.languages.registerSelectionRangeProvider(selector, new MdSmartSelect(engine)), vscode.languages.registerWorkspaceSymbolProvider(new MdWorkspaceSymbolProvider(symbolProvider, workspaceContents)), - vscode.languages.registerReferenceProvider(selector, referencesProvider), - vscode.languages.registerRenameProvider(selector, new MdRenameProvider(referencesProvider, workspaceContents, githubSlugifier)), - vscode.languages.registerDefinitionProvider(selector, new MdDefinitionProvider(referencesProvider)), + vscode.languages.registerRenameProvider(selector, new MdRenameProvider(referencesComputer, workspaceContents, githubSlugifier)), + vscode.languages.registerDefinitionProvider(selector, new MdDefinitionProvider(referencesComputer)), MdPathCompletionProvider.register(selector, engine, linkComputer), registerDocumentLinkProvider(selector, linkComputer), - registerDiagnostics(selector, engine, workspaceContents, linkComputer, commandManager, referencesProvider), + registerDiagnostics(selector, engine, workspaceContents, linkComputer, commandManager, referencesComputer), registerDropIntoEditor(selector), + registerReferencesProvider(selector, referencesComputer), registerPasteProvider(selector), - registerFindFileReferences(commandManager, referencesProvider), + registerFindFileReferences(commandManager, referencesComputer), ); } diff --git a/extensions/markdown-language-features/src/languageFeatures/definitionProvider.ts b/extensions/markdown-language-features/src/languageFeatures/definitionProvider.ts index 622d6bf87a6..dbaeacf1a46 100644 --- a/extensions/markdown-language-features/src/languageFeatures/definitionProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/definitionProvider.ts @@ -5,16 +5,18 @@ import * as vscode from 'vscode'; import { Disposable } from '../util/dispose'; import { SkinnyTextDocument } from '../workspaceContents'; -import { MdReferencesProvider } from './references'; +import { MdReferencesComputer } from './references'; export class MdDefinitionProvider extends Disposable implements vscode.DefinitionProvider { - constructor(private readonly referencesProvider: MdReferencesProvider) { + constructor( + private readonly referencesComputer: MdReferencesComputer + ) { super(); } async provideDefinition(document: SkinnyTextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { - const allRefs = await this.referencesProvider.getAllReferencesAtPosition(document, position, token); + const allRefs = await this.referencesComputer.getReferencesAtPosition(document, position, token); return allRefs.find(ref => ref.kind === 'link' && ref.isDefinition)?.location; } diff --git a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts index ab992258292..f9551d0d0ed 100644 --- a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts @@ -18,7 +18,7 @@ import { ResourceMap } from '../util/resourceMap'; import { MdTableOfContentsWatcher } from '../test/tableOfContentsWatcher'; import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; import { InternalHref, LinkDefinitionSet, MdLink, MdLinkComputer, MdLinkSource } from './documentLinkProvider'; -import { MdReferencesProvider, tryFindMdDocumentForLink } from './references'; +import { MdReferencesComputer, tryFindMdDocumentForLink } from './references'; const localize = nls.loadMessageBundle(); @@ -311,7 +311,7 @@ export class DiagnosticManager extends Disposable { private readonly computer: DiagnosticComputer, private readonly configuration: DiagnosticConfiguration, private readonly reporter: DiagnosticReporter, - private readonly referencesProvider: MdReferencesProvider, + private readonly referencesComputer: MdReferencesComputer, delay = 300, ) { super(); @@ -350,7 +350,7 @@ export class DiagnosticManager extends Disposable { this._register(this.tableOfContentsWatcher.onTocChanged(async e => { // When the toc of a document changes, revalidate every file that linked to it too const triggered = new ResourceMap(); - for (const ref of await this.referencesProvider.getAllReferencesToFile(e.uri, noopToken)) { + for (const ref of await this.referencesComputer.getAllReferencesToFile(e.uri, noopToken)) { const file = ref.location.uri; if (!triggered.has(file)) { this.triggerDiagnostics(file); @@ -627,7 +627,7 @@ export function register( workspaceContents: MdWorkspaceContents, linkComputer: MdLinkComputer, commandManager: CommandManager, - referenceProvider: MdReferencesProvider, + referenceComputer: MdReferencesComputer, ): vscode.Disposable { const configuration = new VSCodeDiagnosticConfiguration(); const manager = new DiagnosticManager( @@ -636,7 +636,7 @@ export function register( new DiagnosticComputer(engine, workspaceContents, linkComputer), configuration, new DiagnosticCollectionReporter(), - referenceProvider); + referenceComputer); return vscode.Disposable.from( configuration, manager, diff --git a/extensions/markdown-language-features/src/languageFeatures/fileReferences.ts b/extensions/markdown-language-features/src/languageFeatures/fileReferences.ts index b9f1321cf06..d6281d9c0ca 100644 --- a/extensions/markdown-language-features/src/languageFeatures/fileReferences.ts +++ b/extensions/markdown-language-features/src/languageFeatures/fileReferences.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import { Command, CommandManager } from '../commandManager'; -import { MdReferencesProvider } from './references'; +import { MdReferencesComputer } from './references'; const localize = nls.loadMessageBundle(); @@ -16,7 +16,7 @@ export class FindFileReferencesCommand implements Command { public readonly id = 'markdown.findAllFileReferences'; constructor( - private readonly referencesProvider: MdReferencesProvider, + private readonly referencesComputer: MdReferencesComputer, ) { } public async execute(resource?: vscode.Uri) { @@ -33,7 +33,7 @@ export class FindFileReferencesCommand implements Command { location: vscode.ProgressLocation.Window, title: localize('progress.title', "Finding file references") }, async (_progress, token) => { - const references = await this.referencesProvider.getAllReferencesToFile(resource!, token); + const references = await this.referencesComputer.getAllReferencesToFile(resource!, token); const locations = references.map(ref => ref.location); const config = vscode.workspace.getConfiguration('references'); @@ -49,6 +49,6 @@ export class FindFileReferencesCommand implements Command { } } -export function registerFindFileReferences(commandManager: CommandManager, referencesProvider: MdReferencesProvider): vscode.Disposable { +export function registerFindFileReferences(commandManager: CommandManager, referencesProvider: MdReferencesComputer): vscode.Disposable { return commandManager.register(new FindFileReferencesCommand(referencesProvider)); } diff --git a/extensions/markdown-language-features/src/languageFeatures/references.ts b/extensions/markdown-language-features/src/languageFeatures/references.ts index 9d02154e649..8e1640b60f4 100644 --- a/extensions/markdown-language-features/src/languageFeatures/references.ts +++ b/extensions/markdown-language-features/src/languageFeatures/references.ts @@ -59,7 +59,7 @@ export interface MdHeaderReference { export type MdReference = MdLinkReference | MdHeaderReference; -export class MdReferencesProvider extends Disposable implements vscode.ReferenceProvider { +export class MdReferencesComputer extends Disposable { private readonly _linkCache: MdWorkspaceCache; @@ -74,15 +74,7 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference this._linkCache = this._register(new MdWorkspaceCache(workspaceContents, doc => linkComputer.getAllLinks(doc, noopToken))); } - async provideReferences(document: SkinnyTextDocument, position: vscode.Position, context: vscode.ReferenceContext, token: vscode.CancellationToken): Promise { - const allRefs = await this.getAllReferencesAtPosition(document, position, token); - - return allRefs - .filter(ref => context.includeDeclaration || !ref.isDefinition) - .map(ref => ref.location); - } - - public async getAllReferencesAtPosition(document: SkinnyTextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { + public async getReferencesAtPosition(document: SkinnyTextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { const toc = await TableOfContents.create(this.engine, document); if (token.isCancellationRequested) { return []; @@ -96,6 +88,11 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference } } + public async getAllReferencesToFile(resource: vscode.Uri, _token: vscode.CancellationToken): Promise { + const allLinksInWorkspace = (await this._linkCache.values()).flat(); + return Array.from(this.findAllLinksToFile(resource, allLinksInWorkspace, undefined)); + } + private async getReferencesToHeader(document: SkinnyTextDocument, header: TocEntry): Promise { const links = (await this._linkCache.values()).flat(); @@ -226,12 +223,7 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference || uri.Utils.extname(href.path) === '' && href.path.with({ path: href.path.path + '.md' }).fsPath === targetDoc.fsPath; } - public async getAllReferencesToFile(resource: vscode.Uri, _token: vscode.CancellationToken): Promise { - const allLinksInWorkspace = (await this._linkCache.values()).flat(); - return Array.from(this.findAllLinksToFile(resource, allLinksInWorkspace, undefined)); - } - - private * findAllLinksToFile(resource: vscode.Uri, allLinksInWorkspace: readonly MdLink[], sourceLink: MdLink | undefined): Iterable { + private *findAllLinksToFile(resource: vscode.Uri, allLinksInWorkspace: readonly MdLink[], sourceLink: MdLink | undefined): Iterable { for (const link of allLinksInWorkspace) { if (link.href.kind !== 'internal' || !this.looksLikeLinkToDoc(link.href, resource)) { continue; @@ -254,7 +246,7 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference } } - private * getReferencesToLinkReference(allLinks: Iterable, refToFind: string, from: { resource: vscode.Uri; range: vscode.Range }): Iterable { + private *getReferencesToLinkReference(allLinks: Iterable, refToFind: string, from: { resource: vscode.Uri; range: vscode.Range }): Iterable { for (const link of allLinks) { let ref: string; if (link.kind === 'definition') { @@ -291,6 +283,30 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference } } +/** + * + */ +export class MdVsCodeReferencesProvider implements vscode.ReferenceProvider { + + public constructor( + private readonly referencesComputer: MdReferencesComputer + ) { } + + async provideReferences(document: SkinnyTextDocument, position: vscode.Position, context: vscode.ReferenceContext, token: vscode.CancellationToken): Promise { + const allRefs = await this.referencesComputer.getReferencesAtPosition(document, position, token); + return allRefs + .filter(ref => context.includeDeclaration || !ref.isDefinition) + .map(ref => ref.location); + } +} + +export function registerReferencesProvider( + selector: vscode.DocumentSelector, + computer: MdReferencesComputer, +): vscode.Disposable { + return vscode.languages.registerReferenceProvider(selector, new MdVsCodeReferencesProvider(computer)); +} + export async function tryFindMdDocumentForLink(href: InternalHref, workspaceContents: MdWorkspaceContents): Promise { const targetDoc = await workspaceContents.getMarkdownDocument(href.path); if (targetDoc) { @@ -305,4 +321,3 @@ export async function tryFindMdDocumentForLink(href: InternalHref, workspaceCont return undefined; } - diff --git a/extensions/markdown-language-features/src/languageFeatures/rename.ts b/extensions/markdown-language-features/src/languageFeatures/rename.ts index 955581b9d97..2fe96c7316c 100644 --- a/extensions/markdown-language-features/src/languageFeatures/rename.ts +++ b/extensions/markdown-language-features/src/languageFeatures/rename.ts @@ -11,7 +11,7 @@ import { Disposable } from '../util/dispose'; import { resolveDocumentLink } from '../util/openDocumentLink'; import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; import { InternalHref } from './documentLinkProvider'; -import { MdHeaderReference, MdLinkReference, MdReference, MdReferencesProvider, tryFindMdDocumentForLink } from './references'; +import { MdHeaderReference, MdLinkReference, MdReference, MdReferencesComputer, tryFindMdDocumentForLink } from './references'; const localize = nls.loadMessageBundle(); @@ -58,7 +58,7 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide private readonly renameNotSupportedText = localize('invalidRenameLocation', "Rename not supported at location"); public constructor( - private readonly referencesProvider: MdReferencesProvider, + private readonly referencesComputer: MdReferencesComputer, private readonly workspaceContents: MdWorkspaceContents, private readonly slugifier: Slugifier, ) { @@ -253,7 +253,7 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide return this.cachedRefs; } - const references = await this.referencesProvider.getAllReferencesAtPosition(document, position, token); + const references = await this.referencesComputer.getReferencesAtPosition(document, position, token); const triggerRef = references.find(ref => ref.isTriggerLocation); if (!triggerRef) { return undefined; diff --git a/extensions/markdown-language-features/src/test/definitionProvider.test.ts b/extensions/markdown-language-features/src/test/definitionProvider.test.ts index cbdf50637ec..7de40fc0f58 100644 --- a/extensions/markdown-language-features/src/test/definitionProvider.test.ts +++ b/extensions/markdown-language-features/src/test/definitionProvider.test.ts @@ -8,7 +8,7 @@ import 'mocha'; import * as vscode from 'vscode'; import { MdDefinitionProvider } from '../languageFeatures/definitionProvider'; import { MdLinkComputer } from '../languageFeatures/documentLinkProvider'; -import { MdReferencesProvider } from '../languageFeatures/references'; +import { MdReferencesComputer } from '../languageFeatures/references'; import { githubSlugifier } from '../slugify'; import { noopToken } from '../util/cancellation'; import { InMemoryDocument } from '../util/inMemoryDocument'; @@ -21,8 +21,8 @@ import { joinLines, workspacePath } from './util'; function getDefinition(doc: InMemoryDocument, pos: vscode.Position, workspaceContents: MdWorkspaceContents) { const engine = createNewMarkdownEngine(); const linkComputer = new MdLinkComputer(engine); - const referencesProvider = new MdReferencesProvider(linkComputer, workspaceContents, engine, githubSlugifier); - const provider = new MdDefinitionProvider(referencesProvider); + const referencesComputer = new MdReferencesComputer(linkComputer, workspaceContents, engine, githubSlugifier); + const provider = new MdDefinitionProvider(referencesComputer); return provider.provideDefinition(doc, pos, noopToken); } diff --git a/extensions/markdown-language-features/src/test/diagnostic.test.ts b/extensions/markdown-language-features/src/test/diagnostic.test.ts index caf88bac3e6..bf164a38523 100644 --- a/extensions/markdown-language-features/src/test/diagnostic.test.ts +++ b/extensions/markdown-language-features/src/test/diagnostic.test.ts @@ -8,7 +8,7 @@ import 'mocha'; import * as vscode from 'vscode'; import { DiagnosticCollectionReporter, DiagnosticComputer, DiagnosticConfiguration, DiagnosticLevel, DiagnosticManager, DiagnosticOptions, DiagnosticReporter } from '../languageFeatures/diagnostics'; import { MdLinkComputer } from '../languageFeatures/documentLinkProvider'; -import { MdReferencesProvider } from '../languageFeatures/references'; +import { MdReferencesComputer } from '../languageFeatures/references'; import { githubSlugifier } from '../slugify'; import { noopToken } from '../util/cancellation'; import { InMemoryDocument } from '../util/inMemoryDocument'; @@ -42,14 +42,14 @@ function createDiagnosticsManager( ) { const engine = createNewMarkdownEngine(); const linkComputer = new MdLinkComputer(engine); - const referencesProvider = new MdReferencesProvider(linkComputer, workspaceContents, engine, githubSlugifier); + const referencesComputer = new MdReferencesComputer(linkComputer, workspaceContents, engine, githubSlugifier); return new DiagnosticManager( engine, workspaceContents, new DiagnosticComputer(engine, workspaceContents, linkComputer), configuration, reporter, - referencesProvider, + referencesComputer, 0); } diff --git a/extensions/markdown-language-features/src/test/fileReferences.test.ts b/extensions/markdown-language-features/src/test/fileReferences.test.ts index da5ed9db282..d160f1efdca 100644 --- a/extensions/markdown-language-features/src/test/fileReferences.test.ts +++ b/extensions/markdown-language-features/src/test/fileReferences.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; import { MdLinkComputer } from '../languageFeatures/documentLinkProvider'; -import { MdReference, MdReferencesProvider } from '../languageFeatures/references'; +import { MdReference, MdReferencesComputer } from '../languageFeatures/references'; import { githubSlugifier } from '../slugify'; import { noopToken } from '../util/cancellation'; import { InMemoryDocument } from '../util/inMemoryDocument'; @@ -20,8 +20,8 @@ import { joinLines, workspacePath } from './util'; function getFileReferences(resource: vscode.Uri, workspaceContents: MdWorkspaceContents) { const engine = createNewMarkdownEngine(); const linkComputer = new MdLinkComputer(engine); - const provider = new MdReferencesProvider(linkComputer, workspaceContents, engine, githubSlugifier); - return provider.getAllReferencesToFile(resource, noopToken); + const computer = new MdReferencesComputer(linkComputer, workspaceContents, engine, githubSlugifier); + return computer.getAllReferencesToFile(resource, noopToken); } function assertReferencesEqual(actualRefs: readonly MdReference[], ...expectedRefs: { uri: vscode.Uri; line: number }[]) { diff --git a/extensions/markdown-language-features/src/test/references.test.ts b/extensions/markdown-language-features/src/test/references.test.ts index 071088883d0..fe93dacb2ab 100644 --- a/extensions/markdown-language-features/src/test/references.test.ts +++ b/extensions/markdown-language-features/src/test/references.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; import { MdLinkComputer } from '../languageFeatures/documentLinkProvider'; -import { MdReferencesProvider } from '../languageFeatures/references'; +import { MdReferencesComputer, MdVsCodeReferencesProvider } from '../languageFeatures/references'; import { githubSlugifier } from '../slugify'; import { noopToken } from '../util/cancellation'; import { InMemoryDocument } from '../util/inMemoryDocument'; @@ -20,7 +20,8 @@ import { joinLines, workspacePath } from './util'; function getReferences(doc: InMemoryDocument, pos: vscode.Position, workspaceContents: MdWorkspaceContents) { const engine = createNewMarkdownEngine(); const linkComputer = new MdLinkComputer(engine); - const provider = new MdReferencesProvider(linkComputer, workspaceContents, engine, githubSlugifier); + const computer = new MdReferencesComputer(linkComputer, workspaceContents, engine, githubSlugifier); + const provider = new MdVsCodeReferencesProvider(computer); return provider.provideReferences(doc, pos, { includeDeclaration: true }, noopToken); } diff --git a/extensions/markdown-language-features/src/test/rename.test.ts b/extensions/markdown-language-features/src/test/rename.test.ts index db523866b1c..0f3e23669b9 100644 --- a/extensions/markdown-language-features/src/test/rename.test.ts +++ b/extensions/markdown-language-features/src/test/rename.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; import { MdLinkComputer } from '../languageFeatures/documentLinkProvider'; -import { MdReferencesProvider } from '../languageFeatures/references'; +import { MdReferencesComputer } from '../languageFeatures/references'; import { MdRenameProvider, MdWorkspaceEdit } from '../languageFeatures/rename'; import { githubSlugifier } from '../slugify'; import { noopToken } from '../util/cancellation'; @@ -24,8 +24,8 @@ import { assertRangeEqual, joinLines, workspacePath } from './util'; function prepareRename(doc: InMemoryDocument, pos: vscode.Position, workspaceContents: MdWorkspaceContents): Promise { const engine = createNewMarkdownEngine(); const linkComputer = new MdLinkComputer(engine); - const referencesProvider = new MdReferencesProvider(linkComputer, workspaceContents, engine, githubSlugifier); - const renameProvider = new MdRenameProvider(referencesProvider, workspaceContents, githubSlugifier); + const referenceComputer = new MdReferencesComputer(linkComputer, workspaceContents, engine, githubSlugifier); + const renameProvider = new MdRenameProvider(referenceComputer, workspaceContents, githubSlugifier); return renameProvider.prepareRename(doc, pos, noopToken); } @@ -35,7 +35,7 @@ function prepareRename(doc: InMemoryDocument, pos: vscode.Position, workspaceCon function getRenameEdits(doc: InMemoryDocument, pos: vscode.Position, newName: string, workspaceContents: MdWorkspaceContents): Promise { const engine = createNewMarkdownEngine(); const linkComputer = new MdLinkComputer(engine); - const referencesProvider = new MdReferencesProvider(linkComputer, workspaceContents, engine, githubSlugifier); + const referencesProvider = new MdReferencesComputer(linkComputer, workspaceContents, engine, githubSlugifier); const renameProvider = new MdRenameProvider(referencesProvider, workspaceContents, githubSlugifier); return renameProvider.provideRenameEditsImpl(doc, pos, newName, noopToken); } From 5d9717967da42628fbb3670d5e180fed4c77122a Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Thu, 16 Jun 2022 11:49:43 -0800 Subject: [PATCH 109/175] add shell integration to tasks (#152242) --- .../common/capabilities/capabilities.ts | 1 + .../commandDetectionCapability.ts | 12 ++++--- src/vs/platform/terminal/common/terminal.ts | 2 +- .../common/xterm/shellIntegrationAddon.ts | 3 ++ .../tasks/browser/terminalTaskSystem.ts | 33 ++++++++++++------- .../terminal/browser/terminalInstance.ts | 15 +++++++-- .../terminal/browser/xterm/decorationAddon.ts | 10 +++--- 7 files changed, 52 insertions(+), 24 deletions(-) diff --git a/src/vs/platform/terminal/common/capabilities/capabilities.ts b/src/vs/platform/terminal/common/capabilities/capabilities.ts index 020de098700..b847384984a 100644 --- a/src/vs/platform/terminal/common/capabilities/capabilities.ts +++ b/src/vs/platform/terminal/common/capabilities/capabilities.ts @@ -96,6 +96,7 @@ export interface ICommandDetectionCapability { readonly onCurrentCommandInvalidated: Event; setCwd(value: string): void; setIsWindowsPty(value: boolean): void; + setIsCommandStorageDisabled(): void; /** * Gets the working directory for a line, this will return undefined if it's unknown in which * case the terminal's initial cwd should be used. diff --git a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts index a67d3fdfc31..b86963763bb 100644 --- a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts +++ b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts @@ -60,7 +60,7 @@ export class CommandDetectionCapability implements ICommandDetectionCapability { private _onCursorMoveListener?: IDisposable; private _commandMarkers: IMarker[] = []; private _dimensions: ITerminalDimensions; - + private __isCommandStorageDisabled: boolean = false; get commands(): readonly ITerminalCommand[] { return this._commands; } get executingCommand(): string | undefined { return this._currentCommand.command; } // TODO: as is unsafe here and it duplicates behavor of executingCommand @@ -248,6 +248,10 @@ export class CommandDetectionCapability implements ICommandDetectionCapability { this._isWindowsPty = value; } + setIsCommandStorageDisabled(): void { + this.__isCommandStorageDisabled = true; + } + getCwdForLine(line: number): string | undefined { // Handle the current partial command first, anything below it's prompt is considered part // of the current command @@ -360,7 +364,7 @@ export class CommandDetectionCapability implements ICommandDetectionCapability { } // Calculate the command - this._currentCommand.command = this._terminal.buffer.active.getLine(this._currentCommand.commandStartMarker.line)?.translateToString(true, this._currentCommand.commandStartX, this._currentCommand.commandRightPromptStartX).trim(); + this._currentCommand.command = this.__isCommandStorageDisabled ? '' : this._terminal.buffer.active.getLine(this._currentCommand.commandStartMarker.line)?.translateToString(true, this._currentCommand.commandStartX, this._currentCommand.commandRightPromptStartX).trim(); let y = this._currentCommand.commandStartMarker.line + 1; const commandExecutedLine = this._currentCommand.commandExecutedMarker.line; for (; y < commandExecutedLine; y++) { @@ -492,7 +496,7 @@ export class CommandDetectionCapability implements ICommandDetectionCapability { startX: undefined, endLine: e.endMarker?.line, executedLine: e.executedMarker?.line, - command: e.command, + command: this.__isCommandStorageDisabled ? '' : e.command, cwd: e.cwd, exitCode: e.exitCode, commandStartLineContent: e.commandStartLineContent, @@ -541,7 +545,7 @@ export class CommandDetectionCapability implements ICommandDetectionCapability { const endMarker = e.endLine !== undefined ? this._terminal.registerMarker(e.endLine - (buffer.baseY + buffer.cursorY)) : undefined; const executedMarker = e.executedLine !== undefined ? this._terminal.registerMarker(e.executedLine - (buffer.baseY + buffer.cursorY)) : undefined; const newCommand = { - command: e.command, + command: this.__isCommandStorageDisabled ? '' : e.command, marker, endMarker, executedMarker, diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 202760e12f6..85b28f35c46 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -439,7 +439,7 @@ export interface IShellLaunchConfig { ignoreConfigurationCwd?: boolean; /** Whether to wait for a key press before closing the terminal. */ - waitOnExit?: boolean | string; + waitOnExit?: boolean | string | ((exitCode: number) => string); /** * A string including ANSI escape sequences that will be written to the terminal emulator diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index 0fd58fb66bc..6bf278467ba 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -238,6 +238,9 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati this._createOrGetCommandDetection(this._terminal).setIsWindowsPty(value === 'True' ? true : false); return true; } + case 'Task': { + this.capabilities.get(TerminalCapability.CommandDetection)?.setIsCommandStorageDisabled(); + } } } } diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index fa46cfb33ef..1c286f2f732 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -55,6 +55,9 @@ import { formatMessageForTerminal } from 'vs/platform/terminal/common/terminalSt import { GroupKind } from 'vs/workbench/contrib/tasks/common/taskConfiguration'; import { Codicon } from 'vs/base/common/codicons'; +const taskShellIntegrationStartSequence = '\x1b]633;A\x07' + '\x1b]633;P;Task=\x07' + '\x1b]633;B\x07'; +const taskShellIntegrationOutputSequence = '\x1b]633;C\x07'; + interface ITerminalData { terminal: ITerminalInstance; lastTask: string; @@ -1012,7 +1015,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { return needsFolderQualification ? task.getQualifiedLabel() : (task.configurationProperties.name || ''); } - private async _createShellLaunchConfig(task: CustomTask | ContributedTask, workspaceFolder: IWorkspaceFolder | undefined, variableResolver: VariableResolver, platform: Platform.Platform, options: CommandOptions, command: CommandString, args: CommandString[], waitOnExit: boolean | string): Promise { + private async _createShellLaunchConfig(task: CustomTask | ContributedTask, workspaceFolder: IWorkspaceFolder | undefined, variableResolver: VariableResolver, platform: Platform.Platform, options: CommandOptions, command: CommandString, args: CommandString[], waitOnExit: boolean | string | ((exitCode: number) => string)): Promise { let shellLaunchConfig: IShellLaunchConfig; const isShellCommand = task.command.runtime === RuntimeType.Shell; const needsFolderQualification = this._contextService.getWorkbenchState() === WorkbenchState.WORKSPACE; @@ -1123,15 +1126,15 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { shellLaunchConfig.args = windowsShellArgs ? combinedShellArgs.join(' ') : combinedShellArgs; if (task.command.presentation && task.command.presentation.echo) { if (needsFolderQualification && workspaceFolder) { - shellLaunchConfig.initialText = formatMessageForTerminal(nls.localize({ + shellLaunchConfig.initialText = taskShellIntegrationStartSequence + formatMessageForTerminal(nls.localize({ key: 'task.executingInFolder', comment: ['The workspace folder the task is running in', 'The task command line or label'] - }, 'Executing task in folder {0}: {1}', workspaceFolder.name, commandLine), { excludeLeadingNewLine: true }); + }, 'Executing task in folder {0}: {1}', workspaceFolder.name, commandLine), { excludeLeadingNewLine: true }) + taskShellIntegrationOutputSequence; } else { - shellLaunchConfig.initialText = formatMessageForTerminal(nls.localize({ + shellLaunchConfig.initialText = taskShellIntegrationStartSequence + formatMessageForTerminal(nls.localize({ key: 'task.executing', comment: ['The task command line or label'] - }, 'Executing task: {0}', commandLine), { excludeLeadingNewLine: true }); + }, 'Executing task: {0}', commandLine), { excludeLeadingNewLine: true }) + taskShellIntegrationOutputSequence; } } } else { @@ -1161,15 +1164,15 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { return args.join(' '); }; if (needsFolderQualification && workspaceFolder) { - shellLaunchConfig.initialText = formatMessageForTerminal(nls.localize({ + shellLaunchConfig.initialText = taskShellIntegrationStartSequence + formatMessageForTerminal(nls.localize({ key: 'task.executingInFolder', comment: ['The workspace folder the task is running in', 'The task command line or label'] - }, 'Executing task in folder {0}: {1}', workspaceFolder.name, `${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)}`), { excludeLeadingNewLine: true }); + }, 'Executing task in folder {0}: {1}', workspaceFolder.name, `${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)}`), { excludeLeadingNewLine: true }) + taskShellIntegrationOutputSequence; } else { - shellLaunchConfig.initialText = formatMessageForTerminal(nls.localize({ + shellLaunchConfig.initialText = taskShellIntegrationStartSequence + formatMessageForTerminal(nls.localize({ key: 'task.executing', comment: ['The task command line or label'] - }, 'Executing task: {0}', `${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)}`), { excludeLeadingNewLine: true }); + }, 'Executing task: {0}', `${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)}`), { excludeLeadingNewLine: true }) + taskShellIntegrationOutputSequence; } } } @@ -1241,7 +1244,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { const options = await this._resolveOptions(resolver, task.command.options); const presentationOptions = task.command.presentation; - let waitOnExit: boolean | string = false; + let waitOnExit: boolean | string | ((exitCode: number) => string) = false; if (!presentationOptions) { throw new Error('Task presentation options should not be undefined here.'); } @@ -1249,9 +1252,9 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { if ((presentationOptions.close === undefined) || (presentationOptions.close === false)) { if ((presentationOptions.reveal !== RevealKind.Never) || !task.configurationProperties.isBackground || (presentationOptions.close === false)) { if (presentationOptions.panel === PanelKind.New) { - waitOnExit = nls.localize('closeTerminal', 'Press any key to close the terminal.'); + waitOnExit = taskShellIntegrationWaitOnExitSequence(nls.localize('closeTerminal', 'Press any key to close the terminal.')); } else if (presentationOptions.showReuseMessage) { - waitOnExit = nls.localize('reuseTerminal', 'Terminal will be reused by tasks, press any key to close it.'); + waitOnExit = taskShellIntegrationWaitOnExitSequence(nls.localize('reuseTerminal', 'Terminal will be reused by tasks, press any key to close it.')); } else { waitOnExit = true; } @@ -1703,3 +1706,9 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { outputChannel?.append(output); } } + +function taskShellIntegrationWaitOnExitSequence(message: string): (exitCode: number) => string { + return (exitCode) => { + return `\x1b]633;D;${exitCode}\x07${message}`; + }; +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 7f9589e2ca3..1d864e5689b 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -1626,8 +1626,15 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { if (exitMessage) { xterm.raw.write(formatMessageForTerminal(exitMessage)); } - if (typeof this._shellLaunchConfig.waitOnExit === 'string') { - xterm.raw.write(formatMessageForTerminal(this._shellLaunchConfig.waitOnExit, { excludeLeadingNewLine: true })); + switch (typeof this._shellLaunchConfig.waitOnExit) { + case 'string': + xterm.raw.write(formatMessageForTerminal(this._shellLaunchConfig.waitOnExit, { excludeLeadingNewLine: true })); + break; + case 'function': + if (this.exitCode !== undefined) { + xterm.raw.write(formatMessageForTerminal(this._shellLaunchConfig.waitOnExit(this.exitCode), { excludeLeadingNewLine: true })); + } + break; } // Disable all input if the terminal is exiting and listen for next keypress xterm.raw.options.disableStdin = true; @@ -1719,7 +1726,9 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this.xterm.raw.options.disableStdin = false; this._isExiting = false; } - this.xterm.clearDecorations(); + if (reset) { + this.xterm.clearDecorations(); + } } // Dispose the environment info widget if it exists diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts index b45bfa99f6d..6354f5f7b64 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts @@ -329,10 +329,12 @@ export class DecorationAddon extends Disposable implements ITerminalAddon { run: () => this._onDidRequestRunCommand.fire({ command, copyAsHtml: true }) }); } - actions.push({ - class: 'rerun-command', tooltip: 'Rerun Command', dispose: () => { }, id: 'terminal.rerunCommand', label: localize("terminal.rerunCommand", 'Rerun Command'), enabled: true, - run: () => this._onDidRequestRunCommand.fire({ command }) - }); + if (command.command !== '') { + actions.push({ + class: 'rerun-command', tooltip: 'Rerun Command', dispose: () => { }, id: 'terminal.rerunCommand', label: localize("terminal.rerunCommand", 'Rerun Command'), enabled: true, + run: () => this._onDidRequestRunCommand.fire({ command }) + }); + } actions.push({ class: 'how-does-this-work', tooltip: 'How does this work?', dispose: () => { }, id: 'terminal.howDoesThisWork', label: localize("terminal.howDoesThisWork", 'How does this work?'), enabled: true, run: () => this._openerService.open('https://code.visualstudio.com/docs/editor/integrated-terminal#_shell-integration') From 9db5a3674ea815acc5c8cfe916c569af977ec845 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Thu, 16 Jun 2022 22:01:19 +0200 Subject: [PATCH 110/175] Bring the nls loader plugin into our sources (#152338) --- .eslintignore | 2 - .eslintrc.json | 2 +- build/filters.js | 2 - build/gulpfile.editor.js | 12 +-- build/lib/bundle.js | 20 +++-- build/lib/bundle.ts | 40 ++++++--- build/lib/optimize.js | 24 ++++-- build/lib/optimize.ts | 31 +++++-- build/lib/standalone.js | 5 +- build/lib/standalone.ts | 5 +- src/buildfile.js | 7 +- src/vs/loader.js | 14 ++- src/vs/nls.build.js | 182 --------------------------------------- src/vs/nls.build.ts | 68 +++++++++++++++ src/vs/nls.d.ts | 25 ------ src/vs/nls.js | 167 ----------------------------------- src/vs/nls.ts | 163 +++++++++++++++++++++++++++++++++++ 17 files changed, 339 insertions(+), 430 deletions(-) delete mode 100644 src/vs/nls.build.js create mode 100644 src/vs/nls.build.ts delete mode 100644 src/vs/nls.d.ts delete mode 100644 src/vs/nls.js create mode 100644 src/vs/nls.ts diff --git a/.eslintignore b/.eslintignore index 829fd016672..e3aedfea0d6 100644 --- a/.eslintignore +++ b/.eslintignore @@ -26,7 +26,5 @@ **/src/vs/*/**/*.d.ts **/src/vs/base/test/common/filters.perf.data.js **/src/vs/loader.js -**/src/vs/nls.build.js -**/src/vs/nls.js **/test/unit/assert.js **/typings/** diff --git a/.eslintrc.json b/.eslintrc.json index a02921b9f6b..e6493f3815e 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -576,7 +576,7 @@ "restrictions": [] }, { - "target": "src/vs/{loader.d.ts,css.ts,css.build.ts,monaco.d.ts,nls.d.ts,nls.mock.ts}", + "target": "src/vs/{loader.d.ts,css.ts,css.build.ts,monaco.d.ts,nls.ts,nls.build.ts,nls.mock.ts}", "restrictions": [] }, { diff --git a/build/filters.js b/build/filters.js index 3d72c28e10b..bee295e70c3 100644 --- a/build/filters.js +++ b/build/filters.js @@ -66,8 +66,6 @@ module.exports.indentationFilter = [ '!**/LICENSE.{txt,rtf}', '!LICENSES.chromium.html', '!**/LICENSE', - '!src/vs/nls.js', - '!src/vs/nls.build.js', '!src/vs/loader.js', '!src/vs/base/browser/dompurify/*', '!src/vs/base/common/marked/marked.js', diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 04b23c2ef17..eb4f55f0801 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -29,14 +29,17 @@ const editorEntryPoints = [ name: 'vs/editor/editor.main', include: [], exclude: ['vs/css', 'vs/nls'], - prepend: ['out-editor-build/vs/css.js', 'out-editor-build/vs/nls.js'], + prepend: [ + { path: 'out-editor-build/vs/css.js' }, + { path: 'out-editor-build/vs/nls.js', amdModuleId: 'vs/nls' } + ], }, { name: 'vs/base/common/worker/simpleWorker', include: ['vs/editor/common/services/editorSimpleWorker'], exclude: ['vs/nls'], - prepend: ['vs/loader.js'], - append: ['vs/base/worker/workerMain'], + prepend: [{ path: 'vs/loader.js' }], + append: [{ path: 'vs/base/worker/workerMain' }], dest: 'vs/base/worker/workerMain.js' } ]; @@ -110,9 +113,6 @@ const createESMSourcesAndResourcesTask = task.define('extract-editor-esm', () => 'inlineEntryPoint:0.ts', 'inlineEntryPoint:1.ts', 'vs/loader.js', - 'vs/nls.ts', - 'vs/nls.build.js', - 'vs/nls.d.ts', 'vs/base/worker/workerMain.ts', ], renames: { diff --git a/build/lib/bundle.js b/build/lib/bundle.js index 4cf08aef429..928b6d1621f 100644 --- a/build/lib/bundle.js +++ b/build/lib/bundle.js @@ -42,14 +42,17 @@ function bundle(entryPoints, config, callback) { if (!config.paths['vs/css']) { config.paths['vs/css'] = 'out-build/vs/css.build'; } + config.buildForceInvokeFactory = config.buildForceInvokeFactory || {}; + config.buildForceInvokeFactory['vs/nls'] = true; + config.buildForceInvokeFactory['vs/css'] = true; loader.config(config); loader(['require'], (localRequire) => { - const resolvePath = (path) => { - const r = localRequire.toUrl(path); + const resolvePath = (entry) => { + const r = localRequire.toUrl(entry.path); if (!/\.js/.test(r)) { - return r + '.js'; + return { path: r + '.js', amdModuleId: entry.amdModuleId }; } - return r; + return { path: r, amdModuleId: entry.amdModuleId }; }; for (const moduleId in entryPointsMap) { const entryPoint = entryPointsMap[moduleId]; @@ -330,10 +333,13 @@ function emitEntryPoint(modulesMap, deps, entryPoint, includedModules, prepend, plugin.writeFile(pluginName, entryPoint, req, write, {}); } }); - const toIFile = (path) => { - const contents = readFileAndRemoveBOM(path); + const toIFile = (entry) => { + let contents = readFileAndRemoveBOM(entry.path); + if (entry.amdModuleId) { + contents = contents.replace(/^define\(/m, `define("${entry.amdModuleId}",`); + } return { - path: path, + path: entry.path, contents: contents }; }; diff --git a/build/lib/bundle.ts b/build/lib/bundle.ts index 4c8a3e14589..c609ef98223 100644 --- a/build/lib/bundle.ts +++ b/build/lib/bundle.ts @@ -42,12 +42,17 @@ interface ILoaderPluginReqFunc { toUrl(something: string): string; } +export interface IExtraFile { + path: string; + amdModuleId?: string; +} + export interface IEntryPoint { name: string; include?: string[]; exclude?: string[]; - prepend?: string[]; - append?: string[]; + prepend?: IExtraFile[]; + append?: IExtraFile[]; dest?: string; } @@ -92,6 +97,13 @@ interface IPartialBundleResult { export interface ILoaderConfig { isBuild?: boolean; paths?: { [path: string]: any }; + /* + * Normally, during a build, no module factories are invoked. This can be used + * to forcefully execute a module's factory. + */ + buildForceInvokeFactory: { + [moduleId: string]: boolean; + }; } /** @@ -132,15 +144,18 @@ export function bundle(entryPoints: IEntryPoint[], config: ILoaderConfig, callba if (!config.paths['vs/css']) { config.paths['vs/css'] = 'out-build/vs/css.build'; } + config.buildForceInvokeFactory = config.buildForceInvokeFactory || {}; + config.buildForceInvokeFactory['vs/nls'] = true; + config.buildForceInvokeFactory['vs/css'] = true; loader.config(config); loader(['require'], (localRequire: any) => { - const resolvePath = (path: string) => { - const r = localRequire.toUrl(path); + const resolvePath = (entry: IExtraFile) => { + const r = localRequire.toUrl(entry.path); if (!/\.js/.test(r)) { - return r + '.js'; + return { path: r + '.js', amdModuleId: entry.amdModuleId }; } - return r; + return { path: r, amdModuleId: entry.amdModuleId }; }; for (const moduleId in entryPointsMap) { const entryPoint = entryPointsMap[moduleId]; @@ -403,8 +418,8 @@ function emitEntryPoint( deps: IGraph, entryPoint: string, includedModules: string[], - prepend: string[], - append: string[], + prepend: IExtraFile[], + append: IExtraFile[], dest: string | undefined ): IEmitEntryPointResult { if (!dest) { @@ -478,10 +493,13 @@ function emitEntryPoint( } }); - const toIFile = (path: string): IFile => { - const contents = readFileAndRemoveBOM(path); + const toIFile = (entry: IExtraFile): IFile => { + let contents = readFileAndRemoveBOM(entry.path); + if (entry.amdModuleId) { + contents = contents.replace(/^define\(/m, `define("${entry.amdModuleId}",`); + } return { - path: path, + path: entry.path, contents: contents }; }; diff --git a/build/lib/optimize.js b/build/lib/optimize.js index d5b957d1195..af5f205d2d4 100644 --- a/build/lib/optimize.js +++ b/build/lib/optimize.js @@ -35,19 +35,25 @@ function loaderConfig() { } exports.loaderConfig = loaderConfig; const IS_OUR_COPYRIGHT_REGEXP = /Copyright \(C\) Microsoft Corporation/i; +function loaderPlugin(src, base, amdModuleId) { + return (gulp + .src(src, { base }) + .pipe(es.through(function (data) { + if (amdModuleId) { + let contents = data.contents.toString('utf8'); + contents = contents.replace(/^define\(/m, `define("${amdModuleId}",`); + data.contents = Buffer.from(contents); + } + this.emit('data', data); + }))); +} function loader(src, bundledFileHeader, bundleLoader, externalLoaderInfo) { - let sources = [ - `${src}/vs/loader.js` - ]; + let loaderStream = gulp.src(`${src}/vs/loader.js`, { base: `${src}` }); if (bundleLoader) { - sources = sources.concat([ - `${src}/vs/css.js`, - `${src}/vs/nls.js` - ]); + loaderStream = es.merge(loaderStream, loaderPlugin(`${src}/vs/css.js`, `${src}`, 'vs/css'), loaderPlugin(`${src}/vs/nls.js`, `${src}`, 'vs/nls')); } let isFirst = true; - return (gulp - .src(sources, { base: `${src}` }) + return (loaderStream .pipe(es.through(function (data) { if (isFirst) { isFirst = false; diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts index fc2a2f42661..09c11015837 100644 --- a/build/lib/optimize.ts +++ b/build/lib/optimize.ts @@ -41,21 +41,34 @@ export function loaderConfig() { const IS_OUR_COPYRIGHT_REGEXP = /Copyright \(C\) Microsoft Corporation/i; +function loaderPlugin(src: string, base: string, amdModuleId: string | undefined): NodeJS.ReadWriteStream { + return ( + gulp + .src(src, { base }) + .pipe(es.through(function (data: VinylFile) { + if (amdModuleId) { + let contents = data.contents.toString('utf8'); + contents = contents.replace(/^define\(/m, `define("${amdModuleId}",`); + data.contents = Buffer.from(contents); + } + this.emit('data', data); + })) + ); +} + function loader(src: string, bundledFileHeader: string, bundleLoader: boolean, externalLoaderInfo?: any): NodeJS.ReadWriteStream { - let sources = [ - `${src}/vs/loader.js` - ]; + let loaderStream = gulp.src(`${src}/vs/loader.js`, { base: `${src}` }); if (bundleLoader) { - sources = sources.concat([ - `${src}/vs/css.js`, - `${src}/vs/nls.js` - ]); + loaderStream = es.merge( + loaderStream, + loaderPlugin(`${src}/vs/css.js`, `${src}`, 'vs/css'), + loaderPlugin(`${src}/vs/nls.js`, `${src}`, 'vs/nls'), + ); } let isFirst = true; return ( - gulp - .src(sources, { base: `${src}` }) + loaderStream .pipe(es.through(function (data) { if (isFirst) { isFirst = false; diff --git a/build/lib/standalone.js b/build/lib/standalone.js index 59d8fee343e..1121e56ff94 100644 --- a/build/lib/standalone.js +++ b/build/lib/standalone.js @@ -106,9 +106,8 @@ function extractEditor(options) { 'vs/css.ts', 'vs/loader.js', 'vs/loader.d.ts', - 'vs/nls.build.js', - 'vs/nls.d.ts', - 'vs/nls.js', + 'vs/nls.build.ts', + 'vs/nls.ts', 'vs/nls.mock.ts', ].forEach(copyFile); } diff --git a/build/lib/standalone.ts b/build/lib/standalone.ts index 34d2e2c9074..f2181e6f274 100644 --- a/build/lib/standalone.ts +++ b/build/lib/standalone.ts @@ -119,9 +119,8 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str 'vs/css.ts', 'vs/loader.js', 'vs/loader.d.ts', - 'vs/nls.build.js', - 'vs/nls.d.ts', - 'vs/nls.js', + 'vs/nls.build.ts', + 'vs/nls.ts', 'vs/nls.mock.ts', ].forEach(copyFile); } diff --git a/src/buildfile.js b/src/buildfile.js index f0f49f65459..2ea007cc376 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -33,8 +33,11 @@ exports.base = [ name: 'vs/editor/common/services/editorSimpleWorker', include: ['vs/base/common/worker/simpleWorker'], exclude: ['vs/nls'], - prepend: ['vs/loader.js', 'vs/nls.js'], - append: ['vs/base/worker/workerMain'], + prepend: [ + { path: 'vs/loader.js' }, + { path: 'vs/nls.js', amdModuleId: 'vs/nls' } + ], + append: [{ path: 'vs/base/worker/workerMain' }], dest: 'vs/base/worker/workerMain.js' }, { diff --git a/src/vs/loader.js b/src/vs/loader.js index 69554c5fac4..38eea8a3ca5 100644 --- a/src/vs/loader.js +++ b/src/vs/loader.js @@ -291,6 +291,9 @@ var AMDLoader; if (typeof options.isBuild !== 'boolean') { options.isBuild = false; } + if (typeof options.buildForceInvokeFactory !== 'object') { + options.buildForceInvokeFactory = {}; + } if (typeof options.paths !== 'object') { options.paths = {}; } @@ -536,6 +539,15 @@ var AMDLoader; Configuration.prototype.isBuild = function () { return this.options.isBuild; }; + Configuration.prototype.shouldInvokeFactory = function (strModuleId) { + if (!this.options.isBuild) { + // outside of a build, all factories should be invoked + return true; + } + // during a build, only explicitly marked or anonymous modules get their factories invoked + return (this.options.buildForceInvokeFactory[strModuleId] + || AMDLoader.Utilities.isAnonymousModule(strModuleId)); + }; /** * Test if module `moduleId` is expected to be defined multiple times */ @@ -1171,7 +1183,7 @@ var AMDLoader; } }; Module._invokeFactory = function (config, strModuleId, callback, dependenciesValues) { - if (config.isBuild() && !AMDLoader.Utilities.isAnonymousModule(strModuleId)) { + if (!config.shouldInvokeFactory(strModuleId)) { return { returnedValue: null, producedError: null diff --git a/src/vs/nls.build.js b/src/vs/nls.build.js deleted file mode 100644 index 0d2929661aa..00000000000 --- a/src/vs/nls.build.js +++ /dev/null @@ -1,182 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -/*--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - * Please make sure to make edits in the .ts file at https://github.com/microsoft/vscode-loader/ - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------*/ -'use strict'; -var _nlsPluginGlobal = this; -var NLSBuildLoaderPlugin; -(function (NLSBuildLoaderPlugin) { - var global = (_nlsPluginGlobal || {}); - var Resources = global.Plugin && global.Plugin.Resources ? global.Plugin.Resources : undefined; - var IS_PSEUDO = (global && global.document && global.document.location && global.document.location.hash.indexOf('pseudo=true') >= 0); - function _format(message, args) { - var result; - if (args.length === 0) { - result = message; - } - else { - result = message.replace(/\{(\d+)\}/g, function (match, rest) { - var index = rest[0]; - return typeof args[index] !== 'undefined' ? args[index] : match; - }); - } - if (IS_PSEUDO) { - // FF3B and FF3D is the Unicode zenkaku representation for [ and ] - result = '\uFF3B' + result.replace(/[aouei]/g, '$&$&') + '\uFF3D'; - } - return result; - } - function findLanguageForModule(config, name) { - var result = config[name]; - if (result) - return result; - result = config['*']; - if (result) - return result; - return null; - } - function localize(data, message) { - var args = []; - for (var _i = 0; _i < (arguments.length - 2); _i++) { - args[_i] = arguments[_i + 2]; - } - return _format(message, args); - } - function createScopedLocalize(scope) { - return function (idx, defaultValue) { - var restArgs = Array.prototype.slice.call(arguments, 2); - return _format(scope[idx], restArgs); - }; - } - var NLSPlugin = /** @class */ (function () { - function NLSPlugin() { - this.localize = localize; - } - NLSPlugin.prototype.setPseudoTranslation = function (value) { - IS_PSEUDO = value; - }; - NLSPlugin.prototype.create = function (key, data) { - return { - localize: createScopedLocalize(data[key]) - }; - }; - NLSPlugin.prototype.load = function (name, req, load, config) { - config = config || {}; - if (!name || name.length === 0) { - load({ - localize: localize - }); - } - else { - var suffix = void 0; - if (Resources && Resources.getString) { - suffix = '.nls.keys'; - req([name + suffix], function (keyMap) { - load({ - localize: function (moduleKey, index) { - if (!keyMap[moduleKey]) - return 'NLS error: unknown key ' + moduleKey; - var mk = keyMap[moduleKey].keys; - if (index >= mk.length) - return 'NLS error unknown index ' + index; - var subKey = mk[index]; - var args = []; - args[0] = moduleKey + '_' + subKey; - for (var _i = 0; _i < (arguments.length - 2); _i++) { - args[_i + 1] = arguments[_i + 2]; - } - return Resources.getString.apply(Resources, args); - } - }); - }); - } - else { - if (config.isBuild) { - req([name + '.nls', name + '.nls.keys'], function (messages, keys) { - NLSPlugin.BUILD_MAP[name] = messages; - NLSPlugin.BUILD_MAP_KEYS[name] = keys; - load(messages); - }); - } - else { - var pluginConfig = config['vs/nls'] || {}; - var language = pluginConfig.availableLanguages ? findLanguageForModule(pluginConfig.availableLanguages, name) : null; - suffix = '.nls'; - if (language !== null && language !== NLSPlugin.DEFAULT_TAG) { - suffix = suffix + '.' + language; - } - req([name + suffix], function (messages) { - if (Array.isArray(messages)) { - messages.localize = createScopedLocalize(messages); - } - else { - messages.localize = createScopedLocalize(messages[name]); - } - load(messages); - }); - } - } - } - }; - NLSPlugin.prototype._getEntryPointsMap = function () { - global.nlsPluginEntryPoints = global.nlsPluginEntryPoints || {}; - return global.nlsPluginEntryPoints; - }; - NLSPlugin.prototype.write = function (pluginName, moduleName, write) { - // getEntryPoint is a Monaco extension to r.js - var entryPoint = write.getEntryPoint(); - // r.js destroys the context of this plugin between calling 'write' and 'writeFile' - // so the only option at this point is to leak the data to a global - var entryPointsMap = this._getEntryPointsMap(); - entryPointsMap[entryPoint] = entryPointsMap[entryPoint] || []; - entryPointsMap[entryPoint].push(moduleName); - if (moduleName !== entryPoint) { - write.asModule(pluginName + '!' + moduleName, 'define([\'vs/nls\', \'vs/nls!' + entryPoint + '\'], function(nls, data) { return nls.create("' + moduleName + '", data); });'); - } - }; - NLSPlugin.prototype.writeFile = function (pluginName, moduleName, req, write, config) { - var entryPointsMap = this._getEntryPointsMap(); - if (entryPointsMap.hasOwnProperty(moduleName)) { - var fileName = req.toUrl(moduleName + '.nls.js'); - var contents = [ - '/*---------------------------------------------------------', - ' * Copyright (c) Microsoft Corporation. All rights reserved.', - ' *--------------------------------------------------------*/' - ], entries = entryPointsMap[moduleName]; - var data = {}; - for (var i = 0; i < entries.length; i++) { - data[entries[i]] = NLSPlugin.BUILD_MAP[entries[i]]; - } - contents.push('define("' + moduleName + '.nls", ' + JSON.stringify(data, null, '\t') + ');'); - write(fileName, contents.join('\r\n')); - } - }; - NLSPlugin.prototype.finishBuild = function (write) { - write('nls.metadata.json', JSON.stringify({ - keys: NLSPlugin.BUILD_MAP_KEYS, - messages: NLSPlugin.BUILD_MAP, - bundles: this._getEntryPointsMap() - }, null, '\t')); - }; - ; - NLSPlugin.DEFAULT_TAG = 'i-default'; - NLSPlugin.BUILD_MAP = {}; - NLSPlugin.BUILD_MAP_KEYS = {}; - return NLSPlugin; - }()); - NLSBuildLoaderPlugin.NLSPlugin = NLSPlugin; - (function () { - define('vs/nls', new NLSPlugin()); - })(); -})(NLSBuildLoaderPlugin || (NLSBuildLoaderPlugin = {})); diff --git a/src/vs/nls.build.ts b/src/vs/nls.build.ts new file mode 100644 index 00000000000..067fba651e3 --- /dev/null +++ b/src/vs/nls.build.ts @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const buildMap: { [name: string]: string[] } = {}; +const buildMapKeys: { [name: string]: string[] } = {}; +const entryPoints: { [entryPoint: string]: string[] } = {}; + +export interface ILocalizeInfo { + key: string; + comment: string[]; +} + +export function localize(data: ILocalizeInfo | string, message: string, ...args: (string | number | boolean | undefined | null)[]): string { + throw new Error(`Not supported at build time!`); +} + +export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void { + if (!name || name.length === 0) { + load({ localize }); + } else { + req([name + '.nls', name + '.nls.keys'], function (messages: string[], keys: string[]) { + buildMap[name] = messages; + buildMapKeys[name] = keys; + load(messages); + }); + } +} + +export function write(pluginName: string, moduleName: string, write: AMDLoader.IPluginWriteCallback): void { + const entryPoint = write.getEntryPoint(); + + entryPoints[entryPoint] = entryPoints[entryPoint] || []; + entryPoints[entryPoint].push(moduleName); + + if (moduleName !== entryPoint) { + write.asModule(pluginName + '!' + moduleName, 'define([\'vs/nls\', \'vs/nls!' + entryPoint + '\'], function(nls, data) { return nls.create("' + moduleName + '", data); });'); + } +} + +export function writeFile(pluginName: string, moduleName: string, req: AMDLoader.IRelativeRequire, write: AMDLoader.IPluginWriteFileCallback, config: AMDLoader.IConfigurationOptions): void { + if (entryPoints.hasOwnProperty(moduleName)) { + const fileName = req.toUrl(moduleName + '.nls.js'); + const contents = [ + '/*---------------------------------------------------------', + ' * Copyright (c) Microsoft Corporation. All rights reserved.', + ' *--------------------------------------------------------*/' + ], + entries = entryPoints[moduleName]; + + const data: { [moduleName: string]: string[] } = {}; + for (let i = 0; i < entries.length; i++) { + data[entries[i]] = buildMap[entries[i]]; + } + + contents.push('define("' + moduleName + '.nls", ' + JSON.stringify(data, null, '\t') + ');'); + write(fileName, contents.join('\r\n')); + } +} + +export function finishBuild(write: AMDLoader.IPluginWriteFileCallback): void { + write('nls.metadata.json', JSON.stringify({ + keys: buildMapKeys, + messages: buildMap, + bundles: entryPoints + }, null, '\t')); +} diff --git a/src/vs/nls.d.ts b/src/vs/nls.d.ts deleted file mode 100644 index c17c85b0191..00000000000 --- a/src/vs/nls.d.ts +++ /dev/null @@ -1,25 +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 interface ILocalizeInfo { - key: string; - comment: string[]; -} - -/** - * Localize a message. - * - * `message` can contain `{n}` notation where it is replaced by the nth value in `...args` - * For example, `localize({ key: 'sayHello', comment: ['Welcomes user'] }, 'hello {0}', name)` - */ -export declare function localize(info: ILocalizeInfo, message: string, ...args: (string | number | boolean | undefined | null)[]): string; - -/** - * Localize a message. - * - * `message` can contain `{n}` notation where it is replaced by the nth value in `...args` - * For example, `localize('sayHello', 'hello {0}', name)` - */ -export declare function localize(key: string, message: string, ...args: (string | number | boolean | undefined | null)[]): string; diff --git a/src/vs/nls.js b/src/vs/nls.js deleted file mode 100644 index 793f0d6eecb..00000000000 --- a/src/vs/nls.js +++ /dev/null @@ -1,167 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -/*--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - * Please make sure to make edits in the .ts file at https://github.com/microsoft/vscode-loader/ - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------*/ -'use strict'; -var __spreadArrays = (this && this.__spreadArrays) || function () { - for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; - for (var r = Array(s), k = 0, i = 0; i < il; i++) - for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) - r[k] = a[j]; - return r; -}; -var NLSLoaderPlugin; -(function (NLSLoaderPlugin) { - var Environment = /** @class */ (function () { - function Environment() { - this._detected = false; - this._isPseudo = false; - } - Object.defineProperty(Environment.prototype, "isPseudo", { - get: function () { - this._detect(); - return this._isPseudo; - }, - enumerable: false, - configurable: true - }); - Environment.prototype._detect = function () { - if (this._detected) { - return; - } - this._detected = true; - this._isPseudo = (typeof document !== 'undefined' && document.location && document.location.hash.indexOf('pseudo=true') >= 0); - }; - return Environment; - }()); - function _format(message, args, env) { - var result; - if (args.length === 0) { - result = message; - } - else { - result = message.replace(/\{(\d+)\}/g, function (match, rest) { - var index = rest[0]; - var arg = args[index]; - var result = match; - if (typeof arg === 'string') { - result = arg; - } - else if (typeof arg === 'number' || typeof arg === 'boolean' || arg === void 0 || arg === null) { - result = String(arg); - } - return result; - }); - } - if (env.isPseudo) { - // FF3B and FF3D is the Unicode zenkaku representation for [ and ] - result = '\uFF3B' + result.replace(/[aouei]/g, '$&$&') + '\uFF3D'; - } - return result; - } - function findLanguageForModule(config, name) { - var result = config[name]; - if (result) - return result; - result = config['*']; - if (result) - return result; - return null; - } - function localize(env, data, message) { - var args = []; - for (var _i = 3; _i < arguments.length; _i++) { - args[_i - 3] = arguments[_i]; - } - return _format(message, args, env); - } - function createScopedLocalize(scope, env) { - return function (idx, defaultValue) { - var restArgs = Array.prototype.slice.call(arguments, 2); - return _format(scope[idx], restArgs, env); - }; - } - var NLSPlugin = /** @class */ (function () { - function NLSPlugin(env) { - var _this = this; - this._env = env; - this.localize = function (data, message) { - var args = []; - for (var _i = 2; _i < arguments.length; _i++) { - args[_i - 2] = arguments[_i]; - } - return localize.apply(void 0, __spreadArrays([_this._env, data, message], args)); - }; - } - NLSPlugin.prototype.setPseudoTranslation = function (value) { - this._env._isPseudo = value; - }; - NLSPlugin.prototype.create = function (key, data) { - return { - localize: createScopedLocalize(data[key], this._env) - }; - }; - NLSPlugin.prototype.load = function (name, req, load, config) { - var _this = this; - var _a; - config = config || {}; - if (!name || name.length === 0) { - load({ - localize: this.localize - }); - } - else { - var pluginConfig = config['vs/nls'] || {}; - var language = pluginConfig.availableLanguages ? findLanguageForModule(pluginConfig.availableLanguages, name) : null; - var suffix = '.nls'; - if (language !== null && language !== NLSPlugin.DEFAULT_TAG) { - suffix = suffix + '.' + language; - } - var messagesLoaded_1 = function (messages) { - if (Array.isArray(messages)) { - messages.localize = createScopedLocalize(messages, _this._env); - } - else { - messages.localize = createScopedLocalize(messages[name], _this._env); - } - load(messages); - }; - if (typeof pluginConfig.loadBundle === 'function') { - pluginConfig.loadBundle(name, language, function (err, messages) { - // We have an error. Load the English default strings to not fail - if (err) { - req([name + '.nls'], messagesLoaded_1); - } - else { - messagesLoaded_1(messages); - } - }); - } - else { - var base = (_a = pluginConfig.baseUrl) !== null && _a !== void 0 ? _a : ''; - req([base + name + suffix], messagesLoaded_1, function (err) { - var _a; - // We have an error. Load the English default strings instead. - console.warn("Falling back to default strings. Unable to load translations because of: " + ((_a = err.message) !== null && _a !== void 0 ? _a : err)); - req([name + '.nls'], messagesLoaded_1); - }); - } - } - }; - NLSPlugin.DEFAULT_TAG = 'i-default'; - return NLSPlugin; - }()); - NLSLoaderPlugin.NLSPlugin = NLSPlugin; - define('vs/nls', new NLSPlugin(new Environment())); -})(NLSLoaderPlugin || (NLSLoaderPlugin = {})); diff --git a/src/vs/nls.ts b/src/vs/nls.ts new file mode 100644 index 00000000000..ebe6f7038ef --- /dev/null +++ b/src/vs/nls.ts @@ -0,0 +1,163 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +let isPseudo = (typeof document !== 'undefined' && document.location && document.location.hash.indexOf('pseudo=true') >= 0); +const DEFAULT_TAG = 'i-default'; + +interface INLSPluginConfig { + availableLanguages?: INLSPluginConfigAvailableLanguages; + loadBundle?: BundleLoader; + baseUrl?: string; +} + +interface INLSPluginConfigAvailableLanguages { + '*'?: string; + [module: string]: string | undefined; +} + +interface BundleLoader { + (bundle: string, locale: string | null, cb: (err: Error, messages: string[] | IBundledStrings) => void): void; +} + +interface IBundledStrings { + [moduleId: string]: string[]; +} + +export interface ILocalizeInfo { + key: string; + comment: string[]; +} + +interface ILocalizeFunc { + (info: ILocalizeInfo, message: string, ...args: (string | number | boolean | undefined | null)[]): string; + (key: string, message: string, ...args: (string | number | boolean | undefined | null)[]): string; +} + +interface IBoundLocalizeFunc { + (idx: number, defaultValue: null): string; +} + +interface IConsumerAPI { + localize: ILocalizeFunc | IBoundLocalizeFunc; +} + +function _format(message: string, args: (string | number | boolean | undefined | null)[]): string { + let result: string; + + if (args.length === 0) { + result = message; + } else { + result = message.replace(/\{(\d+)\}/g, (match, rest) => { + const index = rest[0]; + const arg = args[index]; + let result = match; + if (typeof arg === 'string') { + result = arg; + } else if (typeof arg === 'number' || typeof arg === 'boolean' || arg === void 0 || arg === null) { + result = String(arg); + } + return result; + }); + } + + if (isPseudo) { + // FF3B and FF3D is the Unicode zenkaku representation for [ and ] + result = '\uFF3B' + result.replace(/[aouei]/g, '$&$&') + '\uFF3D'; + } + + return result; +} + +function findLanguageForModule(config: INLSPluginConfigAvailableLanguages, name: string) { + let result = config[name]; + if (result) { + return result; + } + result = config['*']; + if (result) { + return result; + } + return null; +} + +function createScopedLocalize(scope: string[]): IBoundLocalizeFunc { + return function (idx: number, defaultValue: null) { + const restArgs = Array.prototype.slice.call(arguments, 2); + return _format(scope[idx], restArgs); + }; +} + +/** + * Localize a message. + * + * `message` can contain `{n}` notation where it is replaced by the nth value in `...args` + * For example, `localize({ key: 'sayHello', comment: ['Welcomes user'] }, 'hello {0}', name)` + */ +export function localize(info: ILocalizeInfo, message: string, ...args: (string | number | boolean | undefined | null)[]): string; + +/** + * Localize a message. + * + * `message` can contain `{n}` notation where it is replaced by the nth value in `...args` + * For example, `localize('sayHello', 'hello {0}', name)` + */ +export function localize(key: string, message: string, ...args: (string | number | boolean | undefined | null)[]): string; + +export function localize(data: ILocalizeInfo | string, message: string, ...args: (string | number | boolean | undefined | null)[]): string { + return _format(message, args); +} + +export function setPseudoTranslation(value: boolean) { + isPseudo = value; +} + +export function create(key: string, data: IBundledStrings): IConsumerAPI { + return { + localize: createScopedLocalize(data[key]) + }; +} + +export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void { + config = config || {}; + if (!name || name.length === 0) { + load({ + localize: localize + }); + } else { + const pluginConfig = (config['vs/nls'] || {}); + const language = pluginConfig.availableLanguages ? findLanguageForModule(pluginConfig.availableLanguages, name) : null; + let suffix = '.nls'; + if (language !== null && language !== DEFAULT_TAG) { + suffix = suffix + '.' + language; + } + const messagesLoaded = (messages: string[] | IBundledStrings) => { + if (Array.isArray(messages)) { + (messages as any as IConsumerAPI).localize = createScopedLocalize(messages); + } else { + (messages as any as IConsumerAPI).localize = createScopedLocalize(messages[name]); + } + load(messages); + }; + if (typeof pluginConfig.loadBundle === 'function') { + (pluginConfig.loadBundle as BundleLoader)(name, language, (err: Error, messages) => { + // We have an error. Load the English default strings to not fail + if (err) { + req([name + '.nls'], messagesLoaded); + } else { + messagesLoaded(messages); + } + }); + } else { + const base = pluginConfig.baseUrl ?? ''; + req([base + name + suffix], messagesLoaded, (err: Error) => { + // We have an error. Load the English default strings instead. + console.warn(`Falling back to default strings. Unable to load translations because of: ${err.message ?? err}`); + req([name + '.nls'], messagesLoaded); + }); + } + } +} From cb5fa75d119a5a125a3d0d5ca17aee216d560a51 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 16 Jun 2022 13:33:11 -0700 Subject: [PATCH 111/175] Clean up markdown diagnostic tests (#152374) * Clean up markdown diagnostic tests This splits the diagnostic compute tests from the test for the diagnostic manager * Mark internal field as private --- .../src/languageFeatures/diagnostics.ts | 4 +- .../src/test/diagnostic.test.ts | 196 +++++++++--------- 2 files changed, 102 insertions(+), 98 deletions(-) diff --git a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts index f9551d0d0ed..23295b9822b 100644 --- a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts @@ -367,7 +367,7 @@ export class DiagnosticManager extends Disposable { this.pendingDiagnostics.clear(); } - public async recomputeDiagnosticState(doc: SkinnyTextDocument, token: vscode.CancellationToken): Promise<{ diagnostics: readonly vscode.Diagnostic[]; links: readonly MdLink[]; config: DiagnosticOptions }> { + private async recomputeDiagnosticState(doc: SkinnyTextDocument, token: vscode.CancellationToken): Promise<{ diagnostics: readonly vscode.Diagnostic[]; links: readonly MdLink[]; config: DiagnosticOptions }> { const config = this.configuration.getOptions(doc.uri); if (!config.enabled) { return { diagnostics: [], links: [], config }; @@ -456,7 +456,7 @@ export class DiagnosticComputer { public async getDiagnostics(doc: SkinnyTextDocument, options: DiagnosticOptions, token: vscode.CancellationToken): Promise<{ readonly diagnostics: vscode.Diagnostic[]; readonly links: MdLink[] }> { const links = await this.linkComputer.getAllLinks(doc, token); - if (token.isCancellationRequested) { + if (token.isCancellationRequested || !options.enabled) { return { links, diagnostics: [] }; } diff --git a/extensions/markdown-language-features/src/test/diagnostic.test.ts b/extensions/markdown-language-features/src/test/diagnostic.test.ts index bf164a38523..53dd4fb1527 100644 --- a/extensions/markdown-language-features/src/test/diagnostic.test.ts +++ b/extensions/markdown-language-features/src/test/diagnostic.test.ts @@ -11,6 +11,7 @@ import { MdLinkComputer } from '../languageFeatures/documentLinkProvider'; import { MdReferencesComputer } from '../languageFeatures/references'; import { githubSlugifier } from '../slugify'; import { noopToken } from '../util/cancellation'; +import { disposeAll } from '../util/dispose'; import { InMemoryDocument } from '../util/inMemoryDocument'; import { ResourceMap } from '../util/resourceMap'; import { MdWorkspaceContents } from '../workspaceContents'; @@ -18,41 +19,24 @@ import { createNewMarkdownEngine } from './engine'; import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; import { assertRangeEqual, joinLines, workspacePath } from './util'; +const defaultDiagnosticsOptions = Object.freeze({ + enabled: true, + validateFileLinks: DiagnosticLevel.warning, + validateMarkdownFileLinkFragments: undefined, + validateFragmentLinks: DiagnosticLevel.warning, + validateReferences: DiagnosticLevel.warning, + ignoreLinks: [], +}); -async function getComputedDiagnostics(doc: InMemoryDocument, workspaceContents: MdWorkspaceContents): Promise { +async function getComputedDiagnostics(doc: InMemoryDocument, workspaceContents: MdWorkspaceContents, options: Partial = {}): Promise { const engine = createNewMarkdownEngine(); const linkComputer = new MdLinkComputer(engine); const computer = new DiagnosticComputer(engine, workspaceContents, linkComputer); return ( - await computer.getDiagnostics(doc, { - enabled: true, - validateFileLinks: DiagnosticLevel.warning, - validateFragmentLinks: DiagnosticLevel.warning, - validateMarkdownFileLinkFragments: DiagnosticLevel.warning, - validateReferences: DiagnosticLevel.warning, - ignoreLinks: [], - }, noopToken) + await computer.getDiagnostics(doc, { ...defaultDiagnosticsOptions, ...options, }, noopToken) ).diagnostics; } -function createDiagnosticsManager( - workspaceContents: MdWorkspaceContents, - configuration = new MemoryDiagnosticConfiguration({}), - reporter: DiagnosticReporter = new DiagnosticCollectionReporter(), -) { - const engine = createNewMarkdownEngine(); - const linkComputer = new MdLinkComputer(engine); - const referencesComputer = new MdReferencesComputer(linkComputer, workspaceContents, engine, githubSlugifier); - return new DiagnosticManager( - engine, - workspaceContents, - new DiagnosticComputer(engine, workspaceContents, linkComputer), - configuration, - reporter, - referencesComputer, - 0); -} - function assertDiagnosticsEqual(actual: readonly vscode.Diagnostic[], expectedRanges: readonly vscode.Range[]) { assert.strictEqual(actual.length, expectedRanges.length); @@ -61,14 +45,9 @@ function assertDiagnosticsEqual(actual: readonly vscode.Diagnostic[], expectedRa } } -const defaultDiagnosticsOptions = Object.freeze({ - enabled: true, - validateFileLinks: DiagnosticLevel.warning, - validateMarkdownFileLinkFragments: undefined, - validateFragmentLinks: DiagnosticLevel.warning, - validateReferences: DiagnosticLevel.warning, - ignoreLinks: [], -}); +function orderDiagnosticsByRange(diagnostics: Iterable): readonly vscode.Diagnostic[] { + return Array.from(diagnostics).sort((a, b) => a.range.start.compareTo(b.range.start)); +} class MemoryDiagnosticConfiguration implements DiagnosticConfiguration { @@ -110,8 +89,8 @@ class MemoryDiagnosticReporter extends DiagnosticReporter { } } +suite('markdown: Diagnostic Computer', () => { -suite('markdown: Diagnostics', () => { test('Should not return any diagnostics for empty document', async () => { const doc = new InMemoryDocument(workspacePath('doc.md'), joinLines( `text`, @@ -184,7 +163,7 @@ suite('markdown: Diagnostics', () => { )); const diagnostics = await getComputedDiagnostics(doc, new InMemoryWorkspaceMarkdownDocuments([doc])); - assert.deepStrictEqual(diagnostics.length, 0); + assertDiagnosticsEqual(diagnostics, []); }); test('Should generate diagnostics for non-existent link reference', async () => { @@ -207,9 +186,9 @@ suite('markdown: Diagnostics', () => { `[text][no-such-ref]`, )); - const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration({ enabled: false })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1]); + const diagnostics = await getComputedDiagnostics(doc1, workspace, new MemoryDiagnosticConfiguration({ enabled: false }).getOptions(doc1.uri)); + assertDiagnosticsEqual(diagnostics, []); }); test('Should not generate diagnostics for email autolink', async () => { @@ -218,7 +197,7 @@ suite('markdown: Diagnostics', () => { )); const diagnostics = await getComputedDiagnostics(doc1, new InMemoryWorkspaceMarkdownDocuments([doc1])); - assert.deepStrictEqual(diagnostics.length, 0); + assertDiagnosticsEqual(diagnostics, []); }); test('Should not generate diagnostics for html tag that looks like an autolink', async () => { @@ -228,7 +207,7 @@ suite('markdown: Diagnostics', () => { )); const diagnostics = await getComputedDiagnostics(doc1, new InMemoryWorkspaceMarkdownDocuments([doc1])); - assert.deepStrictEqual(diagnostics.length, 0); + assertDiagnosticsEqual(diagnostics, []); }); test('Should allow ignoring invalid file link using glob', async () => { @@ -238,9 +217,9 @@ suite('markdown: Diagnostics', () => { `[text]: /no-such-file`, )); - const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration({ ignoreLinks: ['/no-such-file'] })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1]); + const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['/no-such-file'] }); + assertDiagnosticsEqual(diagnostics, []); }); test('Should be able to disable fragment validation for external files', async () => { @@ -249,11 +228,10 @@ suite('markdown: Diagnostics', () => { )); const doc2 = new InMemoryDocument(workspacePath('doc2.md'), joinLines('')); - const contents = new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]); - const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration({ validateMarkdownFileLinkFragments: DiagnosticLevel.ignore })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); + const diagnostics = await getComputedDiagnostics(doc1, workspace, { validateMarkdownFileLinkFragments: DiagnosticLevel.ignore }); + assertDiagnosticsEqual(diagnostics, []); }); test('Disabling own fragment validation should also disable path fragment validation by default', async () => { @@ -263,17 +241,15 @@ suite('markdown: Diagnostics', () => { )); const doc2 = new InMemoryDocument(workspacePath('doc2.md'), joinLines('')); - const contents = new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]); { - const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration({ validateFragmentLinks: DiagnosticLevel.ignore })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); + const diagnostics = await getComputedDiagnostics(doc1, workspace, { validateFragmentLinks: DiagnosticLevel.ignore }); + assertDiagnosticsEqual(diagnostics, []); } { // But we should be able to override the default - const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration({ validateFragmentLinks: DiagnosticLevel.ignore, validateMarkdownFileLinkFragments: DiagnosticLevel.warning })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); + const diagnostics = await getComputedDiagnostics(doc1, workspace, { validateFragmentLinks: DiagnosticLevel.ignore, validateMarkdownFileLinkFragments: DiagnosticLevel.warning }); assertDiagnosticsEqual(diagnostics, [ new vscode.Range(1, 13, 1, 21), ]); @@ -285,9 +261,10 @@ suite('markdown: Diagnostics', () => { `[text](/no-such-file#header)`, )); - const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration({ ignoreLinks: ['/no-such-file'] })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1]); + + const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['/no-such-file'] }); + assertDiagnosticsEqual(diagnostics, []); }); test('ignoreLinks should not consider link fragment', async () => { @@ -295,9 +272,10 @@ suite('markdown: Diagnostics', () => { `[text](/no-such-file#header)`, )); - const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration({ ignoreLinks: ['/no-such-file'] })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1]); + + const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['/no-such-file'] }); + assertDiagnosticsEqual(diagnostics, []); }); test('ignoreLinks should support globs', async () => { @@ -307,19 +285,19 @@ suite('markdown: Diagnostics', () => { `![i](/images/sub/sub2/ccc.png)`, )); - const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration({ ignoreLinks: ['/images/**/*.png'] })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1]); + const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['/images/**/*.png'] }); + assertDiagnosticsEqual(diagnostics, []); }); test('ignoreLinks should support ignoring header', async () => { const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines( `![i](#no-such)`, )); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1]); - const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration({ ignoreLinks: ['#no-such'] })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); + const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['#no-such'] }); + assertDiagnosticsEqual(diagnostics, []); }); test('ignoreLinks should support ignoring header in file', async () => { @@ -328,16 +306,14 @@ suite('markdown: Diagnostics', () => { )); const doc2 = new InMemoryDocument(workspacePath('doc2.md'), joinLines('')); - const contents = new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]); { - const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration({ ignoreLinks: ['/doc2.md#no-such'] })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); + const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['/doc2.md#no-such'] }); + assertDiagnosticsEqual(diagnostics, []); } { - const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration({ ignoreLinks: ['/doc2.md#*'] })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); + const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['/doc2.md#*'] }); + assertDiagnosticsEqual(diagnostics, []); } }); @@ -347,10 +323,10 @@ suite('markdown: Diagnostics', () => { )); const doc2 = new InMemoryDocument(workspacePath('doc2.md'), joinLines('')); - const contents = new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]); - const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration({ ignoreLinks: ['/doc2.md'] })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]); + + const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['/doc2.md'] }); + assertDiagnosticsEqual(diagnostics, []); }); test('Should not detect checkboxes as invalid links', async () => { @@ -360,10 +336,10 @@ suite('markdown: Diagnostics', () => { `- [ ]`, )); - const contents = new InMemoryWorkspaceMarkdownDocuments([doc1]); - const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration({ ignoreLinks: ['/doc2.md'] })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1]); + + const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['/doc2.md'] }); + assertDiagnosticsEqual(diagnostics, []); }); test('Should detect invalid links with titles', async () => { @@ -404,20 +380,19 @@ suite('markdown: Diagnostics', () => { }); test('Own header link using file path link should be controlled by "validateMarkdownFileLinkFragments" instead of "validateFragmentLinks"', async () => { - const doc = new InMemoryDocument(workspacePath('sub', 'doc.md'), joinLines( + const doc1 = new InMemoryDocument(workspacePath('sub', 'doc.md'), joinLines( `[bad](doc.md#no-such)`, `[bad](doc#no-such)`, `[bad](/sub/doc.md#no-such)`, `[bad](/sub/doc#no-such)`, )); - const contents = new InMemoryWorkspaceMarkdownDocuments([doc]); - const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration({ + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1]); + + const diagnostics = await getComputedDiagnostics(doc1, workspace, { validateFragmentLinks: DiagnosticLevel.ignore, validateMarkdownFileLinkFragments: DiagnosticLevel.warning, - })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc, noopToken); - + }); assertDiagnosticsEqual(orderDiagnosticsByRange(diagnostics), [ new vscode.Range(0, 12, 0, 20), new vscode.Range(1, 9, 1, 17), @@ -425,6 +400,39 @@ suite('markdown: Diagnostics', () => { new vscode.Range(3, 14, 3, 22), ]); }); +}); + +suite('Markdown: Diagnostics manager', () => { + + const _disposables: vscode.Disposable[] = []; + + setup(() => { + disposeAll(_disposables); + }); + + teardown(() => { + disposeAll(_disposables); + }); + + function createDiagnosticsManager( + workspace: MdWorkspaceContents, + configuration = new MemoryDiagnosticConfiguration({}), + reporter: DiagnosticReporter = new DiagnosticCollectionReporter(), + ) { + const engine = createNewMarkdownEngine(); + const linkComputer = new MdLinkComputer(engine); + const referencesComputer = new MdReferencesComputer(linkComputer, workspace, engine, githubSlugifier); + const manager = new DiagnosticManager( + engine, + workspace, + new DiagnosticComputer(engine, workspace, linkComputer), + configuration, + reporter, + referencesComputer, + 0); + _disposables.push(manager, referencesComputer); + return manager; + } test('Should revalidate linked files when header changes', async () => { const doc1Uri = workspacePath('doc1.md'); @@ -439,10 +447,10 @@ suite('markdown: Diagnostics', () => { `[text](#no-such-2)`, )); - const contents = new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]); const reporter = new MemoryDiagnosticReporter(); - const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration({}), reporter); + const manager = createDiagnosticsManager(workspace, new MemoryDiagnosticConfiguration({}), reporter); await manager.ready; // Check initial state @@ -455,7 +463,7 @@ suite('markdown: Diagnostics', () => { ]); // Edit header - contents.updateDocument(new InMemoryDocument(doc2Uri, joinLines( + workspace.updateDocument(new InMemoryDocument(doc2Uri, joinLines( `# new header`, `[text](#new-header)`, `[text](#no-such-2)`, @@ -470,7 +478,7 @@ suite('markdown: Diagnostics', () => { ]); // Revert to original file - contents.updateDocument(new InMemoryDocument(doc2Uri, joinLines( + workspace.updateDocument(new InMemoryDocument(doc2Uri, joinLines( `# header`, `[text](#header)`, `[text](#no-such-2)`, @@ -484,7 +492,3 @@ suite('markdown: Diagnostics', () => { ]); }); }); - -function orderDiagnosticsByRange(diagnostics: Iterable): readonly vscode.Diagnostic[] { - return Array.from(diagnostics).sort((a, b) => a.range.start.compareTo(b.range.start)); -} From c4623596524062960569781f5d023641be3da063 Mon Sep 17 00:00:00 2001 From: "Babak K. Shandiz" Date: Thu, 16 Jun 2022 21:01:30 +0000 Subject: [PATCH 112/175] Add option to always reveal tests after state change (#152331) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ⚙️ Add `testing.alwaysRevealTestOnStateChange` to config parameters Signed-off-by: Babak K. Shandiz * 🔨 Reveal test (after state change) respecting the always-reveal option Signed-off-by: Babak K. Shandiz * 📜 Improve description for `testing.alwaysRevealTestOnStateChange` Signed-off-by: Babak K. Shandiz --- .../contrib/testing/browser/testingExplorerView.ts | 7 ++++++- src/vs/workbench/contrib/testing/common/configuration.ts | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index 9ab3bc94bd8..0c639a29c53 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -554,6 +554,11 @@ export class TestingExplorerViewModel extends Disposable { followRunningTests = getTestingConfiguration(configurationService, TestingConfigKeys.FollowRunningTest); })); + let alwaysRevealTestAfterStateChange = getTestingConfiguration(configurationService, TestingConfigKeys.AlwaysRevealTestOnStateChange); + this._register(configurationService.onDidChangeConfiguration(() => { + alwaysRevealTestAfterStateChange = getTestingConfiguration(configurationService, TestingConfigKeys.AlwaysRevealTestOnStateChange); + })); + this._register(testResults.onTestChanged(evt => { if (!followRunningTests) { return; @@ -569,7 +574,7 @@ export class TestingExplorerViewModel extends Disposable { return; } - this.revealById(evt.item.item.extId, false, false); + this.revealById(evt.item.item.extId, alwaysRevealTestAfterStateChange, false); })); this._register(testResults.onResultsChanged(() => { diff --git a/src/vs/workbench/contrib/testing/common/configuration.ts b/src/vs/workbench/contrib/testing/common/configuration.ts index e9ab2c0ac56..eabfe6204c1 100644 --- a/src/vs/workbench/contrib/testing/common/configuration.ts +++ b/src/vs/workbench/contrib/testing/common/configuration.ts @@ -17,6 +17,7 @@ export const enum TestingConfigKeys { DefaultGutterClickAction = 'testing.defaultGutterClickAction', GutterEnabled = 'testing.gutterEnabled', SaveBeforeTest = 'testing.saveBeforeTest', + AlwaysRevealTestOnStateChange = 'testing.alwaysRevealTestOnStateChange' } export const enum AutoOpenTesting { @@ -128,6 +129,11 @@ export const testingConfiguation: IConfigurationNode = { default: 'openOnTestStart', description: localize('testing.openTesting', "Controls when the testing view should open.") }, + [TestingConfigKeys.AlwaysRevealTestOnStateChange]: { + markdownDescription: localize('testing.alwaysRevealTestOnStateChange', "Always reveal the executed test when `#testing.followRunningTest#` is on. If this setting is turned off, only failed tests will be revealed."), + type: 'boolean', + default: false, + }, } }; @@ -141,6 +147,7 @@ export interface ITestingConfiguration { [TestingConfigKeys.GutterEnabled]: boolean; [TestingConfigKeys.SaveBeforeTest]: boolean; [TestingConfigKeys.OpenTesting]: AutoOpenTesting; + [TestingConfigKeys.AlwaysRevealTestOnStateChange]: boolean; } export const getTestingConfiguration = (config: IConfigurationService, key: K) => config.getValue(key); From 0d160ac2376d4c3573ec9620614d625eeb7a5694 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Thu, 16 Jun 2022 23:05:10 +0200 Subject: [PATCH 113/175] Fix bundling (#152389) * Ensure stable order in bundled loader.js * Avoid packaging build version of loader plugins --- build/lib/bundle.js | 9 ++++++--- build/lib/bundle.ts | 9 ++++++--- build/lib/optimize.js | 40 ++++++++++++++++++++++++++-------------- build/lib/optimize.ts | 40 +++++++++++++++++++++++++++------------- 4 files changed, 65 insertions(+), 33 deletions(-) diff --git a/build/lib/bundle.js b/build/lib/bundle.js index 928b6d1621f..497ac4fb67e 100644 --- a/build/lib/bundle.js +++ b/build/lib/bundle.js @@ -48,10 +48,13 @@ function bundle(entryPoints, config, callback) { loader.config(config); loader(['require'], (localRequire) => { const resolvePath = (entry) => { - const r = localRequire.toUrl(entry.path); - if (!/\.js/.test(r)) { - return { path: r + '.js', amdModuleId: entry.amdModuleId }; + let r = localRequire.toUrl(entry.path); + if (!r.endsWith('.js')) { + r += '.js'; } + // avoid packaging the build version of plugins: + r = r.replace('vs/nls.build.js', 'vs/nls.js'); + r = r.replace('vs/css.build.js', 'vs/css.js'); return { path: r, amdModuleId: entry.amdModuleId }; }; for (const moduleId in entryPointsMap) { diff --git a/build/lib/bundle.ts b/build/lib/bundle.ts index c609ef98223..c5fdc2da18c 100644 --- a/build/lib/bundle.ts +++ b/build/lib/bundle.ts @@ -151,10 +151,13 @@ export function bundle(entryPoints: IEntryPoint[], config: ILoaderConfig, callba loader(['require'], (localRequire: any) => { const resolvePath = (entry: IExtraFile) => { - const r = localRequire.toUrl(entry.path); - if (!/\.js/.test(r)) { - return { path: r + '.js', amdModuleId: entry.amdModuleId }; + let r = localRequire.toUrl(entry.path); + if (!r.endsWith('.js')) { + r += '.js'; } + // avoid packaging the build version of plugins: + r = r.replace('vs/nls.build.js', 'vs/nls.js'); + r = r.replace('vs/css.build.js', 'vs/css.js'); return { path: r, amdModuleId: entry.amdModuleId }; }; for (const moduleId in entryPointsMap) { diff --git a/build/lib/optimize.js b/build/lib/optimize.js index af5f205d2d4..6a0b1804834 100644 --- a/build/lib/optimize.js +++ b/build/lib/optimize.js @@ -52,29 +52,41 @@ function loader(src, bundledFileHeader, bundleLoader, externalLoaderInfo) { if (bundleLoader) { loaderStream = es.merge(loaderStream, loaderPlugin(`${src}/vs/css.js`, `${src}`, 'vs/css'), loaderPlugin(`${src}/vs/nls.js`, `${src}`, 'vs/nls')); } - let isFirst = true; + const files = []; + const order = (f) => { + if (f.path.endsWith('loader.js')) { + return 0; + } + if (f.path.endsWith('css.js')) { + return 1; + } + if (f.path.endsWith('nls.js')) { + return 2; + } + return 3; + }; return (loaderStream .pipe(es.through(function (data) { - if (isFirst) { - isFirst = false; - this.emit('data', new VinylFile({ - path: 'fake', - base: '.', - contents: Buffer.from(bundledFileHeader) - })); - this.emit('data', data); - } - else { - this.emit('data', data); - } + files.push(data); }, function () { + files.sort((a, b) => { + return order(a) - order(b); + }); + files.unshift(new VinylFile({ + path: 'fake', + base: '.', + contents: Buffer.from(bundledFileHeader) + })); if (externalLoaderInfo !== undefined) { - this.emit('data', new VinylFile({ + files.push(new VinylFile({ path: 'fake2', base: '.', contents: Buffer.from(`require.config(${JSON.stringify(externalLoaderInfo, undefined, 2)});`) })); } + for (const file of files) { + this.emit('data', file); + } this.emit('end'); })) .pipe(concat('vs/loader.js'))); diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts index 09c11015837..96f002faae8 100644 --- a/build/lib/optimize.ts +++ b/build/lib/optimize.ts @@ -66,29 +66,43 @@ function loader(src: string, bundledFileHeader: string, bundleLoader: boolean, e ); } - let isFirst = true; + const files: VinylFile[] = []; + const order = (f: VinylFile) => { + if (f.path.endsWith('loader.js')) { + return 0; + } + if (f.path.endsWith('css.js')) { + return 1; + } + if (f.path.endsWith('nls.js')) { + return 2; + } + return 3; + }; + return ( loaderStream .pipe(es.through(function (data) { - if (isFirst) { - isFirst = false; - this.emit('data', new VinylFile({ - path: 'fake', - base: '.', - contents: Buffer.from(bundledFileHeader) - })); - this.emit('data', data); - } else { - this.emit('data', data); - } + files.push(data); }, function () { + files.sort((a, b) => { + return order(a) - order(b); + }); + files.unshift(new VinylFile({ + path: 'fake', + base: '.', + contents: Buffer.from(bundledFileHeader) + })); if (externalLoaderInfo !== undefined) { - this.emit('data', new VinylFile({ + files.push(new VinylFile({ path: 'fake2', base: '.', contents: Buffer.from(`require.config(${JSON.stringify(externalLoaderInfo, undefined, 2)});`) })); } + for (const file of files) { + this.emit('data', file); + } this.emit('end'); })) .pipe(concat('vs/loader.js')) From 1a2c2b2916da6110ca2da0aad604a477a8aa03e9 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Thu, 16 Jun 2022 15:00:08 -0700 Subject: [PATCH 114/175] Use full titlebar height for menubar button hit area (Fix #150170) (#151250) * Use full titlebar height for menubar button hit area (Fix #150170) * Fix "more" toggle padding * fix focus outlines * Fix outline border * Fix "regular" menu button outline offset Co-authored-by: SteVen Batten --- src/vs/base/browser/ui/menu/menubar.css | 39 ++++++++++++------- .../browser/parts/titlebar/menubarControl.ts | 19 ++++----- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/vs/base/browser/ui/menu/menubar.css b/src/vs/base/browser/ui/menu/menubar.css index c4a57a11c0f..64e309bf5dc 100644 --- a/src/vs/base/browser/ui/menu/menubar.css +++ b/src/vs/base/browser/ui/menu/menubar.css @@ -10,9 +10,7 @@ flex-shrink: 1; box-sizing: border-box; height: 100%; - padding: 4px 0; overflow: hidden; - flex-wrap: wrap; } .fullscreen .menubar:not(.compact) { @@ -21,15 +19,21 @@ } .menubar > .menubar-menu-button { + display: flex; align-items: center; box-sizing: border-box; - padding: 0px 8px; - border-radius: 5px; cursor: default; -webkit-app-region: no-drag; zoom: 1; white-space: nowrap; - outline: 0; + outline: 0 !important; +} + +.monaco-workbench .menubar:not(.compact) > .menubar-menu-button:focus .menubar-menu-title { + outline-width: 1px; + outline-style: solid; + outline-offset: -1px; + outline-color: var(--vscode-focusBorder); } .menubar.compact { @@ -43,6 +47,11 @@ padding: 0px; } +.menubar-menu-title { + padding: 0px 8px; + border-radius: 5px; +} + .menubar .menubar-menu-items-holder { position: fixed; left: 0px; @@ -64,8 +73,13 @@ } .menubar .toolbar-toggle-more { - width: 20px; - height: 100%; + width: 22px; + height: 22px; + padding: 0 8px; + display: flex; + align-items: center; + justify-content: center; + vertical-align: sub; } .menubar.compact .toolbar-toggle-more { @@ -79,19 +93,14 @@ justify-content: center; } -.menubar .toolbar-toggle-more { - padding: 0; - vertical-align: sub; -} - .menubar.compact .toolbar-toggle-more::before { content: "\eb94" !important; } /* Match behavior of outline for activity bar icons */ -.menubar.compact > .menubar-menu-button.open, -.menubar.compact > .menubar-menu-button:focus, -.menubar.compact > .menubar-menu-button:hover { +.menubar.compact > .menubar-menu-button.open .menubar-menu-title, +.menubar.compact > .menubar-menu-button:focus .menubar-menu-title, +.menubar.compact > .menubar-menu-button:hover .menubar-menu-title{ outline-width: 1px !important; outline-offset: -8px !important; } diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index 464c1e59ef6..27bd3301696 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -481,9 +481,9 @@ export class CustomMenubarControl extends MenubarControl { const menubarSelectedBgColor = theme.getColor(MENUBAR_SELECTION_BACKGROUND); if (menubarSelectedBgColor) { collector.addRule(` - .monaco-workbench .menubar:not(.compact) > .menubar-menu-button.open, - .monaco-workbench .menubar:not(.compact) > .menubar-menu-button:focus, - .monaco-workbench .menubar:not(:focus-within):not(.compact) > .menubar-menu-button:hover { + .monaco-workbench .menubar:not(.compact) > .menubar-menu-button.open .menubar-menu-title, + .monaco-workbench .menubar:not(.compact) > .menubar-menu-button:focus .menubar-menu-title, + .monaco-workbench .menubar:not(:focus-within):not(.compact) > .menubar-menu-button:hover .menubar-menu-title { background-color: ${menubarSelectedBgColor}; } `); @@ -492,19 +492,20 @@ export class CustomMenubarControl extends MenubarControl { const menubarSelectedBorderColor = theme.getColor(MENUBAR_SELECTION_BORDER); if (menubarSelectedBorderColor) { collector.addRule(` - .monaco-workbench .menubar > .menubar-menu-button:hover { + .monaco-workbench .menubar > .menubar-menu-button:hover .menubar-menu-title { outline: dashed 1px; } - .monaco-workbench .menubar > .menubar-menu-button.open, - .monaco-workbench .menubar > .menubar-menu-button:focus { + .monaco-workbench .menubar > .menubar-menu-button.open .menubar-menu-title, + .monaco-workbench .menubar > .menubar-menu-button:focus .menubar-menu-title { outline: solid 1px; } - .monaco-workbench .menubar > .menubar-menu-button.open, - .monaco-workbench .menubar > .menubar-menu-button:focus, - .monaco-workbench .menubar > .menubar-menu-button:hover { + .monaco-workbench .menubar > .menubar-menu-button.open .menubar-menu-title, + .monaco-workbench .menubar > .menubar-menu-button:focus .menubar-menu-title, + .monaco-workbench .menubar > .menubar-menu-button:hover .menubar-menu-title { outline-color: ${menubarSelectedBorderColor}; + outline-offset: -1px; } `); } From eab44be0c110a9c58a6cec7632f1da0d33357b7d Mon Sep 17 00:00:00 2001 From: andreamah Date: Thu, 16 Jun 2022 15:06:14 -0700 Subject: [PATCH 115/175] Edited shortcuts and cleanup for debug console changing --- .../contrib/debug/browser/debug.contribution.ts | 4 ++-- .../workbench/contrib/debug/browser/debugCommands.ts | 12 +++++++----- 2 files changed, 9 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 966e52b75ae..1f9a2e611c3 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -120,8 +120,8 @@ registerDebugCommandPaletteItem(TOGGLE_INLINE_BREAKPOINT_ID, nls.localize('inlin registerDebugCommandPaletteItem(DEBUG_START_COMMAND_ID, DEBUG_START_LABEL, ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE.notEqualsTo(getStateLabel(State.Initializing)))); registerDebugCommandPaletteItem(DEBUG_RUN_COMMAND_ID, DEBUG_RUN_LABEL, ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE.notEqualsTo(getStateLabel(State.Initializing)))); registerDebugCommandPaletteItem(SELECT_AND_START_ID, SELECT_AND_START_LABEL, ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE.notEqualsTo(getStateLabel(State.Initializing)))); -registerDebugCommandPaletteItem(NEXT_DEBUG_CONSOLE_ID, NEXT_DEBUG_CONSOLE_LABEL, CONTEXT_IN_DEBUG_MODE); -registerDebugCommandPaletteItem(PREV_DEBUG_CONSOLE_ID, PREV_DEBUG_CONSOLE_LABEL, CONTEXT_IN_DEBUG_MODE); +registerDebugCommandPaletteItem(NEXT_DEBUG_CONSOLE_ID, NEXT_DEBUG_CONSOLE_LABEL); +registerDebugCommandPaletteItem(PREV_DEBUG_CONSOLE_ID, PREV_DEBUG_CONSOLE_LABEL); // Debug callstack context menu diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index 78e6c595726..6c095958a70 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -142,6 +142,7 @@ function isSessionContext(obj: any): obj is CallStackContext { async function changeDebugConsoleFocus(accessor: ServicesAccessor, next: boolean) { const debugService = accessor.get(IDebugService); + const viewsService = accessor.get(IViewsService); const sessions = debugService.getModel().getSessions(true).filter(s => s.hasSeparateRepl()); let currSession = debugService.getViewModel().focusedSession; @@ -162,7 +163,6 @@ async function changeDebugConsoleFocus(accessor: ServicesAccessor, next: boolean } await debugService.focusStackFrame(undefined, undefined, sessions[nextIndex], { explicit: true }); - const viewsService = accessor.get(IViewsService); if (!viewsService.isViewVisible(REPL_VIEW_ID)) { await viewsService.openView(REPL_VIEW_ID, true); } @@ -262,9 +262,10 @@ MenuRegistry.appendMenuItem(MenuId.EditorContext, { KeybindingsRegistry.registerCommandAndKeybindingRule({ id: NEXT_DEBUG_CONSOLE_ID, - weight: KeybindingWeight.WorkbenchContrib, + weight: KeybindingWeight.WorkbenchContrib + 1, when: CONTEXT_IN_DEBUG_REPL, - primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.BracketRight, + primary: KeyMod.CtrlCmd | KeyCode.PageDown, + mac: { primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.BracketRight }, handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { changeDebugConsoleFocus(accessor, true); } @@ -272,9 +273,10 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: PREV_DEBUG_CONSOLE_ID, - weight: KeybindingWeight.WorkbenchContrib, + weight: KeybindingWeight.WorkbenchContrib + 1, when: CONTEXT_IN_DEBUG_REPL, - primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.BracketLeft, + primary: KeyMod.CtrlCmd | KeyCode.PageUp, + mac: { primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.BracketLeft }, handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { changeDebugConsoleFocus(accessor, false); } From 974016415953b570bd73866fb69a76ae5369a070 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 17 Jun 2022 00:13:30 +0200 Subject: [PATCH 116/175] Use `UtilityProcess` to spawn extension hosts and add a setting (#152344) Fixes #150825: Use `UtilityProcess` to spawn extension hosts and add a setting --- .../contrib/extensions/browser/extensions.contribution.ts | 5 +++++ .../electron-browser/nativeLocalProcessExtensionHost.ts | 3 +-- .../extensions/electron-sandbox/localProcessExtensionHost.ts | 2 ++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index d9650884885..6fbc52d18ba 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -224,6 +224,11 @@ Registry.as(ConfigurationExtensions.Configuration) } }] }, + 'extensions.experimental.useUtilityProcess': { + type: 'boolean', + description: localize('extensionsUseUtilityProcess', "When enabled, the extension host will be launched using the new UtilityProcess Electron API."), + default: true + }, [WORKSPACE_TRUST_EXTENSION_SUPPORT]: { type: 'object', scope: ConfigurationScope.APPLICATION, diff --git a/src/vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost.ts index a7a35d0d022..d5bcf07f039 100644 --- a/src/vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost.ts @@ -10,7 +10,6 @@ import { StopWatch } from 'vs/base/common/stopwatch'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net'; import { createRandomIPCHandle, NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; -import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { IExtensionHostProcessOptions } from 'vs/platform/extensions/common/extensionHostStarter'; import { ILogService } from 'vs/platform/log/common/log'; import { createMessageOfType, MessageType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; @@ -19,7 +18,7 @@ import { ExtensionHostProcess, ExtHostMessagePortCommunication, IExtHostCommunic export class NativeLocalProcessExtensionHost extends SandboxLocalProcessExtensionHost { protected override async _start(): Promise { const canUseUtilityProcess = await this._extensionHostStarter.canUseUtilityProcess(); - if (canUseUtilityProcess && process.env['VSCODE_USE_UTILITY_PROCESS']) { + if (canUseUtilityProcess && this._configurationService.getValue('extensions.experimental.useUtilityProcess')) { const communication = this._toDispose.add(new ExtHostMessagePortCommunication(this._logService)); return this._startWithCommunication(communication); } else { diff --git a/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts index 58e38c744c8..cb7abfcbf53 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts @@ -44,6 +44,7 @@ import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { generateUuid } from 'vs/base/common/uuid'; import { acquirePort } from 'vs/base/parts/ipc/electron-sandbox/ipc.mp'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export interface ILocalProcessExtensionHostInitData { readonly autoStart: boolean; @@ -150,6 +151,7 @@ export class SandboxLocalProcessExtensionHost implements IExtensionHost { @IProductService private readonly _productService: IProductService, @IShellEnvironmentService private readonly _shellEnvironmentService: IShellEnvironmentService, @IExtensionHostStarter protected readonly _extensionHostStarter: IExtensionHostStarter, + @IConfigurationService protected readonly _configurationService: IConfigurationService, ) { const devOpts = parseExtensionDevOptions(this._environmentService); this._isExtensionDevHost = devOpts.isExtensionDevHost; From fb11d9c69ad8d7f2896237472b243a65feafbb6f Mon Sep 17 00:00:00 2001 From: andreamah Date: Thu, 16 Jun 2022 15:17:04 -0700 Subject: [PATCH 117/175] add Focus word to debug console actions --- src/vs/workbench/contrib/debug/browser/debugCommands.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index 6c095958a70..eddb769f3df 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -75,8 +75,8 @@ export const SELECT_AND_START_LABEL = nls.localize('selectAndStartDebugging', "S export const DEBUG_CONFIGURE_LABEL = nls.localize('openLaunchJson', "Open '{0}'", 'launch.json'); export const DEBUG_START_LABEL = nls.localize('startDebug', "Start Debugging"); export const DEBUG_RUN_LABEL = nls.localize('startWithoutDebugging', "Start Without Debugging"); -export const NEXT_DEBUG_CONSOLE_LABEL = nls.localize('nextDebugConsole', "Next Debug Console"); -export const PREV_DEBUG_CONSOLE_LABEL = nls.localize('prevDebugConsole', "Previous Debug Console"); +export const NEXT_DEBUG_CONSOLE_LABEL = nls.localize('nextDebugConsole', "Focus Next Debug Console"); +export const PREV_DEBUG_CONSOLE_LABEL = nls.localize('prevDebugConsole', "Focus Previous Debug Console"); interface CallStackContext { sessionId: string; From afe316c08c00fd4900893e0bc74b9d4f82788cfb Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 16 Jun 2022 15:53:19 -0700 Subject: [PATCH 118/175] Add diagnostic manager enablement change tests (#152392) * Add test for MD diagnostic manager config changes Add a simple test the enabling/disable diagnostics should make the diagnostic manager recompute diagnostics * Add `.get` --- .../src/languageFeatures/diagnostics.ts | 41 +++++---- .../src/test/diagnostic.test.ts | 86 +++++++++++++++---- 2 files changed, 90 insertions(+), 37 deletions(-) diff --git a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts index 23295b9822b..686bfe7c241 100644 --- a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts @@ -231,7 +231,7 @@ class LinkDoesNotExistDiagnostic extends vscode.Diagnostic { } export abstract class DiagnosticReporter extends Disposable { - private readonly pending = new ResourceMap>(); + private readonly pending = new Set>(); public clear(): void { this.pending.clear(); @@ -239,20 +239,15 @@ export abstract class DiagnosticReporter extends Disposable { public abstract set(uri: vscode.Uri, diagnostics: readonly vscode.Diagnostic[]): void; - public delete(uri: vscode.Uri): void { - this.pending.delete(uri); + public abstract delete(uri: vscode.Uri): void; + + public addWorkItem(promise: Promise): Promise { + this.pending.add(promise); + promise.finally(() => this.pending.delete(promise)); + return promise; } - public signalTriggered(uri: vscode.Uri, recompute: Promise): void { - this.pending.set(uri, recompute); - recompute.finally(() => { - if (this.pending.get(uri) === recompute) { - this.pending.delete(uri); - } - }); - } - - public async waitAllPending(): Promise { + public async waitPendingWork(): Promise { await Promise.all([...this.pending.values()]); } } @@ -276,8 +271,7 @@ export class DiagnosticCollectionReporter extends DiagnosticReporter { this.collection.set(uri, tabs.has(uri) ? diagnostics : []); } - public override delete(uri: vscode.Uri): void { - super.delete(uri); + public delete(uri: vscode.Uri): void { this.collection.delete(uri); } @@ -391,21 +385,26 @@ export class DiagnosticManager extends Disposable { })); } - private async rebuild() { + private rebuild(): Promise { this.reporter.clear(); this.pendingDiagnostics.clear(); this.inFlightDiagnostics.clear(); - for (const doc of await this.workspaceContents.getAllMarkdownDocuments()) { - this.triggerDiagnostics(doc.uri); - } + return this.reporter.addWorkItem( + (async () => { + const allDocs = await this.workspaceContents.getAllMarkdownDocuments(); + await Promise.all(Array.from(allDocs, doc => this.triggerDiagnostics(doc.uri))); + })() + ); } - private triggerDiagnostics(uri: vscode.Uri) { + private async triggerDiagnostics(uri: vscode.Uri): Promise { this.inFlightDiagnostics.cancel(uri); this.pendingDiagnostics.add(uri); - this.reporter.signalTriggered(uri, this.diagnosticDelayer.trigger(() => this.recomputePendingDiagnostics())); + return this.reporter.addWorkItem( + this.diagnosticDelayer.trigger(() => this.recomputePendingDiagnostics()) + ); } } diff --git a/extensions/markdown-language-features/src/test/diagnostic.test.ts b/extensions/markdown-language-features/src/test/diagnostic.test.ts index 53dd4fb1527..e98dd6fbd0a 100644 --- a/extensions/markdown-language-features/src/test/diagnostic.test.ts +++ b/extensions/markdown-language-features/src/test/diagnostic.test.ts @@ -54,20 +54,27 @@ class MemoryDiagnosticConfiguration implements DiagnosticConfiguration { private readonly _onDidChange = new vscode.EventEmitter(); public readonly onDidChange = this._onDidChange.event; - constructor( - private readonly _options: Partial, - ) { } + private _options: Partial; - getOptions(_resource: vscode.Uri): DiagnosticOptions { + constructor(options: Partial) { + this._options = options; + } + + public getOptions(_resource: vscode.Uri): DiagnosticOptions { return { ...defaultDiagnosticsOptions, ...this._options, }; } + + public update(newOptions: Partial) { + this._options = newOptions; + this._onDidChange.fire(); + } } class MemoryDiagnosticReporter extends DiagnosticReporter { - public readonly diagnostics = new ResourceMap(); + private readonly diagnostics = new ResourceMap(); override dispose(): void { super.clear(); @@ -83,10 +90,13 @@ class MemoryDiagnosticReporter extends DiagnosticReporter { this.diagnostics.set(uri, diagnostics); } - override delete(uri: vscode.Uri): void { - super.delete(uri); + delete(uri: vscode.Uri): void { this.diagnostics.delete(uri); } + + get(uri: vscode.Uri): readonly vscode.Diagnostic[] { + return orderDiagnosticsByRange(this.diagnostics.get(uri) ?? []); + } } suite('markdown: Diagnostic Computer', () => { @@ -434,6 +444,50 @@ suite('Markdown: Diagnostics manager', () => { return manager; } + test('Changing enable/disable should recompute diagnostics', async () => { + const doc1Uri = workspacePath('doc1.md'); + const doc2Uri = workspacePath('doc2.md'); + const workspace = new InMemoryWorkspaceMarkdownDocuments([ + new InMemoryDocument(doc1Uri, joinLines( + `[text](#no-such-1)`, + )), + new InMemoryDocument(doc2Uri, joinLines( + `[text](#no-such-2)`, + )) + ]); + + const reporter = new MemoryDiagnosticReporter(); + const config = new MemoryDiagnosticConfiguration({ enabled: true }); + + const manager = createDiagnosticsManager(workspace, config, reporter); + await manager.ready; + + // Check initial state (Enabled) + await reporter.waitPendingWork(); + assertDiagnosticsEqual(reporter.get(doc1Uri), [ + new vscode.Range(0, 7, 0, 17), + ]); + assertDiagnosticsEqual(reporter.get(doc2Uri), [ + new vscode.Range(0, 7, 0, 17), + ]); + + // Disable + config.update({ enabled: false }); + await reporter.waitPendingWork(); + assertDiagnosticsEqual(reporter.get(doc1Uri), []); + assertDiagnosticsEqual(reporter.get(doc2Uri), []); + + // Enable + config.update({ enabled: true }); + await reporter.waitPendingWork(); + assertDiagnosticsEqual(reporter.get(doc1Uri), [ + new vscode.Range(0, 7, 0, 17), + ]); + assertDiagnosticsEqual(reporter.get(doc2Uri), [ + new vscode.Range(0, 7, 0, 17), + ]); + }); + test('Should revalidate linked files when header changes', async () => { const doc1Uri = workspacePath('doc1.md'); const doc1 = new InMemoryDocument(doc1Uri, joinLines( @@ -454,11 +508,11 @@ suite('Markdown: Diagnostics manager', () => { await manager.ready; // Check initial state - await reporter.waitAllPending(); - assertDiagnosticsEqual(reporter.diagnostics.get(doc1Uri)!, [ + await reporter.waitPendingWork(); + assertDiagnosticsEqual(reporter.get(doc1Uri), [ new vscode.Range(0, 7, 0, 15), ]); - assertDiagnosticsEqual(reporter.diagnostics.get(doc2Uri)!, [ + assertDiagnosticsEqual(reporter.get(doc2Uri), [ new vscode.Range(2, 7, 2, 17), ]); @@ -468,12 +522,12 @@ suite('Markdown: Diagnostics manager', () => { `[text](#new-header)`, `[text](#no-such-2)`, ))); - await reporter.waitAllPending(); - assertDiagnosticsEqual(orderDiagnosticsByRange(reporter.diagnostics.get(doc1Uri)!), [ + await reporter.waitPendingWork(); + assertDiagnosticsEqual(reporter.get(doc1Uri), [ new vscode.Range(0, 7, 0, 15), new vscode.Range(1, 15, 1, 22), ]); - assertDiagnosticsEqual(reporter.diagnostics.get(doc2Uri)!, [ + assertDiagnosticsEqual(reporter.get(doc2Uri), [ new vscode.Range(2, 7, 2, 17), ]); @@ -483,11 +537,11 @@ suite('Markdown: Diagnostics manager', () => { `[text](#header)`, `[text](#no-such-2)`, ))); - await reporter.waitAllPending(); - assertDiagnosticsEqual(orderDiagnosticsByRange(reporter.diagnostics.get(doc1Uri)!), [ + await reporter.waitPendingWork(); + assertDiagnosticsEqual(reporter.get(doc1Uri), [ new vscode.Range(0, 7, 0, 15) ]); - assertDiagnosticsEqual(reporter.diagnostics.get(doc2Uri)!, [ + assertDiagnosticsEqual(reporter.get(doc2Uri), [ new vscode.Range(2, 7, 2, 17), ]); }); From 7025c6e71c76008ecd88c1620cb36807360b7f90 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Thu, 16 Jun 2022 19:11:33 -0400 Subject: [PATCH 119/175] Include more of the shims folder in attempt to fix build (#152398) --- build/.moduleignore | 2 +- build/.webignore | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/.moduleignore b/build/.moduleignore index 73c3f623db8..79514212c66 100644 --- a/build/.moduleignore +++ b/build/.moduleignore @@ -124,7 +124,7 @@ prebuild-install/**/* @microsoft/applicationinsights*/** @microsoft/dynamicproto-js/** -!@microsoft/applicationinsights-shims/dist/** +!@microsoft/applicationinsights-shims/** !@microsoft/applicationinsights-web/dist/applicationinsights-web.min.js # other node modules diff --git a/build/.webignore b/build/.webignore index 3aaafbceb21..a47a109b68c 100644 --- a/build/.webignore +++ b/build/.webignore @@ -35,5 +35,5 @@ xterm-addon-webgl/out/** @microsoft/applicationinsights*/** @microsoft/dynamicproto-js/** -!@microsoft/applicationinsights-shims/dist/** +!@microsoft/applicationinsights-shims/** !@microsoft/applicationinsights-web/dist/applicationinsights-web.min.js From bfa57bdad5012d29e437e97747b4128d33e0bbf6 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 16 Jun 2022 13:57:38 -0700 Subject: [PATCH 120/175] Restore old buffers if no additional interactions happened This makes terminals only restore their current session's buffer if there was some interaction by the user, similar to how the initial process doesn't get revived if no interaction occurred. Fixes #142855 --- src/vs/platform/terminal/node/ptyService.ts | 72 ++++++++++++++++----- 1 file changed, 55 insertions(+), 17 deletions(-) diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index ddadd6a069d..f7014d2a80c 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -152,7 +152,8 @@ export class PtyService extends Disposable implements IPtyService { true, terminal.processDetails.workspaceId, terminal.processDetails.workspaceName, - true + true, + terminal.replayEvent.events[0].data ); // Don't start the process here as there's no terminal to answer CPR this._revivedPtyIdMap.set(terminal.id, { newId, state: terminal }); @@ -175,7 +176,8 @@ export class PtyService extends Disposable implements IPtyService { shouldPersist: boolean, workspaceId: string, workspaceName: string, - isReviving?: boolean + isReviving?: boolean, + rawReviveBuffer?: string ): Promise { if (shellLaunchConfig.attachPersistentProcess) { throw new Error('Attempt to create a process when attach object was provided'); @@ -188,7 +190,7 @@ export class PtyService extends Disposable implements IPtyService { executableEnv, options }; - const persistentProcess = new PersistentTerminalProcess(id, process, workspaceId, workspaceName, shouldPersist, cols, rows, processLaunchOptions, unicodeVersion, this._reconnectConstants, this._logService, isReviving ? shellLaunchConfig.initialText : undefined, shellLaunchConfig.icon, shellLaunchConfig.color, shellLaunchConfig.name, shellLaunchConfig.fixedDimensions); + const persistentProcess = new PersistentTerminalProcess(id, process, workspaceId, workspaceName, shouldPersist, cols, rows, processLaunchOptions, unicodeVersion, this._reconnectConstants, this._logService, isReviving ? shellLaunchConfig.initialText : undefined, rawReviveBuffer, shellLaunchConfig.icon, shellLaunchConfig.color, shellLaunchConfig.name, shellLaunchConfig.fixedDimensions); process.onDidChangeProperty(property => this._onDidChangeProperty.fire({ id, property })); process.onProcessExit(event => { persistentProcess.dispose(); @@ -409,6 +411,15 @@ export class PtyService extends Disposable implements IPtyService { } } +const enum InteractionState { + /** The terminal has not been interacted with. */ + None = 0, + /** The terminal has only been interacted with by the replay mechanism. */ + ReplayOnly = 1, + /** The terminal has been directly interacted with this session. */ + Session = 2 +} + export class PersistentTerminalProcess extends Disposable { private readonly _bufferer: TerminalDataBufferer; @@ -417,7 +428,7 @@ export class PersistentTerminalProcess extends Disposable { private readonly _pendingCommands = new Map void; reject: (err: any) => void }>(); private _isStarted: boolean = false; - private _hasWrittenData: boolean = false; + private _interactionState: InteractionState = InteractionState.None; private _orphanQuestionBarrier: AutoOpenBarrier | null; private _orphanQuestionReplyTime: number; @@ -451,7 +462,7 @@ export class PersistentTerminalProcess extends Disposable { get pid(): number { return this._pid; } get shellLaunchConfig(): IShellLaunchConfig { return this._terminalProcess.shellLaunchConfig; } - get hasWrittenData(): boolean { return this._hasWrittenData; } + get hasWrittenData(): boolean { return this._interactionState !== InteractionState.None; } get title(): string { return this._title || this._terminalProcess.currentTitle; } get titleSource(): TitleEventSource { return this._titleSource; } get icon(): TerminalIcon | undefined { return this._icon; } @@ -459,13 +470,21 @@ export class PersistentTerminalProcess extends Disposable { get fixedDimensions(): IFixedTerminalDimensions | undefined { return this._fixedDimensions; } setTitle(title: string, titleSource: TitleEventSource): void { - this._hasWrittenData = true; + if (titleSource === TitleEventSource.Api) { + this._interactionState = InteractionState.Session; + this._serializer.freeRawReviveBuffer(); + } this._title = title; this._titleSource = titleSource; } setIcon(icon: TerminalIcon, color?: string): void { - this._hasWrittenData = true; + if (!this._icon || 'id' in icon && 'id' in this._icon && icon.id !== this._icon.id || + !this.color || color !== this._color) { + + this._serializer.freeRawReviveBuffer(); + this._interactionState = InteractionState.Session; + } this._icon = icon; this._color = color; } @@ -487,6 +506,7 @@ export class PersistentTerminalProcess extends Disposable { reconnectConstants: IReconnectConstants, private readonly _logService: ILogService, reviveBuffer: string | undefined, + rawReviveBuffer: string | undefined, private _icon?: TerminalIcon, private _color?: string, name?: string, @@ -504,6 +524,7 @@ export class PersistentTerminalProcess extends Disposable { reconnectConstants.scrollback, unicodeVersion, reviveBuffer, + rawReviveBuffer, this._logService ); this._fixedDimensions = fixedDimensions; @@ -559,7 +580,7 @@ export class PersistentTerminalProcess extends Disposable { } serializeNormalBuffer(): Promise { - return this._serializer.generateReplayEvent(true); + return this._serializer.generateReplayEvent(true, this._interactionState !== InteractionState.Session); } async refreshProperty(type: T): Promise { @@ -604,7 +625,8 @@ export class PersistentTerminalProcess extends Disposable { return this._terminalProcess.shutdown(immediate); } input(data: string): void { - this._hasWrittenData = true; + this._interactionState = InteractionState.Session; + this._serializer.freeRawReviveBuffer(); if (this._inReplay) { return; } @@ -652,7 +674,9 @@ export class PersistentTerminalProcess extends Disposable { } async triggerReplay(): Promise { - this._hasWrittenData = true; + if (this._interactionState === InteractionState.None) { + this._interactionState = InteractionState.ReplayOnly; + } const ev = await this._serializer.generateReplayEvent(); let dataLength = 0; for (const e of ev.events) { @@ -736,18 +760,24 @@ class XtermSerializer implements ITerminalSerializer { rows: number, scrollback: number, unicodeVersion: '6' | '11', - reviveBuffer: string | undefined, + reviveBufferWithRestoreMessage: string | undefined, + private _rawReviveBuffer: string | undefined, logService: ILogService ) { this._xterm = new XtermTerminal({ cols, rows, scrollback }); - if (reviveBuffer) { - this._xterm.writeln(reviveBuffer); + if (reviveBufferWithRestoreMessage) { + this._xterm.writeln(reviveBufferWithRestoreMessage); } this.setUnicodeVersion(unicodeVersion); this._shellIntegrationAddon = new ShellIntegrationAddon(true, undefined, logService); this._xterm.loadAddon(this._shellIntegrationAddon); } + freeRawReviveBuffer(): void { + // Free the memory if the terminal if it will need to be re-serialized + this._rawReviveBuffer = undefined; + } + handleData(data: string): void { this._xterm.write(data); } @@ -756,15 +786,22 @@ class XtermSerializer implements ITerminalSerializer { this._xterm.resize(cols, rows); } - async generateReplayEvent(normalBufferOnly?: boolean): Promise { + async generateReplayEvent(normalBufferOnly?: boolean, restoreToLastReviveBuffer?: boolean): Promise { const serialize = new (await this._getSerializeConstructor()); this._xterm.loadAddon(serialize); - const options: ISerializeOptions = { scrollback: this._xterm.getOption('scrollback') }; + const options: ISerializeOptions = { + scrollback: this._xterm.getOption('scrollback') + }; if (normalBufferOnly) { options.excludeAltBuffer = true; options.excludeModes = true; } - const serialized = serialize.serialize(options); + let serialized: string; + if (restoreToLastReviveBuffer && this._rawReviveBuffer) { + serialized = this._rawReviveBuffer; + } else { + serialized = serialize.serialize(options); + } return { events: [ { @@ -831,7 +868,8 @@ function printTime(ms: number): string { export interface ITerminalSerializer { handleData(data: string): void; + freeRawReviveBuffer(): void; handleResize(cols: number, rows: number): void; - generateReplayEvent(normalBufferOnly?: boolean): Promise; + generateReplayEvent(normalBufferOnly?: boolean, restoreToLastReviveBuffer?: boolean): Promise; setUnicodeVersion?(version: '6' | '11'): void; } From 9467bd00f46fa9862df82f172b0c47a876f5cb23 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Thu, 16 Jun 2022 16:17:19 -0800 Subject: [PATCH 121/175] allow for terminal arrow navigation when in accessibility mode (#152255) --- .../terminal/browser/terminalActions.ts | 24 +++++++++++++++---- .../terminal/browser/terminalInstance.ts | 4 +++- .../browser/xterm/navigationModeAddon.ts | 8 ++++--- .../terminal/common/terminalContextKey.ts | 6 +++++ 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 90d402ce3ed..da2439feeef 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -733,14 +733,22 @@ export function registerTerminalActions() { title: { value: localize('workbench.action.terminal.navigationModeFocusPrevious', "Focus Previous Line (Navigation Mode)"), original: 'Focus Previous Line (Navigation Mode)' }, f1: true, category, - keybinding: { + keybinding: [{ + primary: KeyCode.UpArrow, + when: ContextKeyExpr.or( + ContextKeyExpr.and(TerminalContextKeys.a11yTreeFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED, TerminalContextKeys.navigationModeActive), + ContextKeyExpr.and(TerminalContextKeys.focus, CONTEXT_ACCESSIBILITY_MODE_ENABLED, TerminalContextKeys.navigationModeActive), + ), + weight: KeybindingWeight.WorkbenchContrib + }, + { primary: KeyMod.CtrlCmd | KeyCode.UpArrow, when: ContextKeyExpr.or( ContextKeyExpr.and(TerminalContextKeys.a11yTreeFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED), ContextKeyExpr.and(TerminalContextKeys.focus, CONTEXT_ACCESSIBILITY_MODE_ENABLED) ), weight: KeybindingWeight.WorkbenchContrib - }, + }], precondition: TerminalContextKeys.processSupported }); } @@ -755,14 +763,22 @@ export function registerTerminalActions() { title: { value: localize('workbench.action.terminal.navigationModeFocusNext', "Focus Next Line (Navigation Mode)"), original: 'Focus Next Line (Navigation Mode)' }, f1: true, category, - keybinding: { + keybinding: [{ + primary: KeyCode.DownArrow, + when: ContextKeyExpr.or( + ContextKeyExpr.and(TerminalContextKeys.a11yTreeFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED, TerminalContextKeys.navigationModeActive), + ContextKeyExpr.and(TerminalContextKeys.focus, CONTEXT_ACCESSIBILITY_MODE_ENABLED, TerminalContextKeys.navigationModeActive), + ), + weight: KeybindingWeight.WorkbenchContrib + }, + { primary: KeyMod.CtrlCmd | KeyCode.DownArrow, when: ContextKeyExpr.or( ContextKeyExpr.and(TerminalContextKeys.a11yTreeFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED), ContextKeyExpr.and(TerminalContextKeys.focus, CONTEXT_ACCESSIBILITY_MODE_ENABLED) ), weight: KeybindingWeight.WorkbenchContrib - }, + }], precondition: TerminalContextKeys.processSupported }); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 1d864e5689b..e1ca09c0b67 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -175,6 +175,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { private _horizontalScrollbar: DomScrollableElement | undefined; private _terminalHasTextContextKey: IContextKey; private _terminalA11yTreeFocusContextKey: IContextKey; + private _navigationModeActiveContextKey: IContextKey; private _cols: number = 0; private _rows: number = 0; private _fixedCols: number | undefined; @@ -407,6 +408,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._terminalHasTextContextKey = TerminalContextKeys.textSelected.bindTo(this._contextKeyService); this._terminalA11yTreeFocusContextKey = TerminalContextKeys.a11yTreeFocus.bindTo(this._contextKeyService); + this._navigationModeActiveContextKey = TerminalContextKeys.navigationModeActive.bindTo(this._contextKeyService); this._terminalAltBufferActiveContextKey = TerminalContextKeys.altBufferActive.bindTo(this._contextKeyService); this._logService.trace(`terminalInstance#ctor (instanceId: ${this.instanceId})`, this._shellLaunchConfig); @@ -1827,7 +1829,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { updateAccessibilitySupport(): void { const isEnabled = this._accessibilityService.isScreenReaderOptimized(); if (isEnabled) { - this._navigationModeAddon = new NavigationModeAddon(this._terminalA11yTreeFocusContextKey); + this._navigationModeAddon = new NavigationModeAddon(this._terminalA11yTreeFocusContextKey, this._navigationModeActiveContextKey); this.xterm!.raw.loadAddon(this._navigationModeAddon); } else { this._navigationModeAddon?.dispose(); diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/navigationModeAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/navigationModeAddon.ts index 8487459af57..34f3f4937d9 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/navigationModeAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/navigationModeAddon.ts @@ -12,7 +12,8 @@ export class NavigationModeAddon implements INavigationMode, ITerminalAddon { private _terminal: Terminal | undefined; constructor( - private _navigationModeContextKey: IContextKey + private _navigationModeContextKey: IContextKey, + private _navigationModeActiveContextKey: IContextKey ) { } activate(terminal: Terminal): void { @@ -27,13 +28,14 @@ export class NavigationModeAddon implements INavigationMode, ITerminalAddon { } this._terminal.scrollToBottom(); this._terminal.focus(); + this._navigationModeActiveContextKey.set(false); } focusPreviousLine(): void { if (!this._terminal || !this._terminal.element) { return; } - + this._navigationModeActiveContextKey.set(true); // Focus previous row if a row is already focused if (document.activeElement && document.activeElement.parentElement && document.activeElement.parentElement.classList.contains('xterm-accessibility-tree')) { const element = document.activeElement.previousElementSibling; @@ -76,7 +78,7 @@ export class NavigationModeAddon implements INavigationMode, ITerminalAddon { if (!this._terminal || !this._terminal.element) { return; } - + this._navigationModeActiveContextKey.set(true); // Focus previous row if a row is already focused if (document.activeElement && document.activeElement.parentElement && document.activeElement.parentElement.classList.contains('xterm-accessibility-tree')) { const element = document.activeElement.nextElementSibling; diff --git a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts index 553c0edf9df..72f1792fc71 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts @@ -22,6 +22,7 @@ export const enum TerminalContextKeyStrings { TabsMouse = 'terminalTabsMouse', AltBufferActive = 'terminalAltBufferActive', A11yTreeFocus = 'terminalA11yTreeFocus', + NavigationModeActive = 'terminalNavigationModeActive', ViewShowing = 'terminalViewShowing', TextSelected = 'terminalTextSelected', FindVisible = 'terminalFindVisible', @@ -84,6 +85,11 @@ export namespace TerminalContextKeys { /** Whether the user is navigating a terminal's the accessibility tree. */ export const a11yTreeFocus = new RawContextKey(TerminalContextKeyStrings.A11yTreeFocus, false, true); + /** + * Whether the user is currently in navigation mode + */ + export const navigationModeActive = new RawContextKey(TerminalContextKeyStrings.NavigationModeActive, false, true); + /** Whether text is selected in the active terminal. */ export const textSelected = new RawContextKey(TerminalContextKeyStrings.TextSelected, false, localize('terminalTextSelectedContextKey', "Whether text is selected in the active terminal.")); From cced28f08057bc08c6579939253c8e8d90359604 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 16 Jun 2022 17:12:00 -0700 Subject: [PATCH 122/175] Prevent terminals being attached to by multiple windows I was no able to reproduce the issue where users were seeing a terminal get reconnected to from multiple windows. This is a defensive change that does the following: - When a persistent process is being attached to, check if it is disconnected. If so, confirm that it is orphaned, continue if so and throw if not. - Silently recreate a new process with warning logs if an attach fails. Fixes #133542 --- src/vs/platform/terminal/node/ptyService.ts | 13 ++++++++++-- .../browser/terminalProcessManager.ts | 20 +++++++++++-------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index ddadd6a069d..5d6b474148c 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -210,10 +210,11 @@ export class PtyService extends Disposable implements IPtyService { async attachToProcess(id: number): Promise { try { - this._throwIfNoPty(id).attach(); + await this._throwIfNoPty(id).attach(); this._logService.trace(`Persistent process reconnection "${id}"`); } catch (e) { this._logService.trace(`Persistent process reconnection "${id}" failed`, e.message); + throw e; } } @@ -543,8 +544,16 @@ export class PersistentTerminalProcess extends Disposable { })); } - attach(): void { + async attach(): Promise { this._logService.trace('persistentTerminalProcess#attach', this._persistentProcessId); + if (!this._disconnectRunner1.isScheduled() && !this._disconnectRunner2.isScheduled()) { + // Something wrong happened if the disconnect runner is not canceled, this likely means + // multiple windows attempted to attach. + if (!await this._isOrphaned()) { + throw new Error(`Cannot attach to persistent process "${this._persistentProcessId}", it is already adopted`); + } + this._logService.warn(`Persistent process "${this._persistentProcessId}": Process had no disconnect runners but was an orphan`); + } this._disconnectRunner1.cancel(); this._disconnectRunner2.cancel(); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index bba89cdbcee..3e2725c70ff 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -210,7 +210,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce this._dimensions.rows = rows; this._isScreenReaderModeEnabled = isScreenReaderModeEnabled; - let newProcess: ITerminalChildProcess; + let newProcess: ITerminalChildProcess | undefined; if (shellLaunchConfig.customPtyImplementation) { this._processType = ProcessType.PsuedoTerminal; @@ -251,10 +251,12 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce if (result) { newProcess = result; } else { - this._logService.trace(`Attach to process failed for terminal ${shellLaunchConfig.attachPersistentProcess}`); - return undefined; + // Warn and just create a new terminal if attach failed for some reason + this._logService.warn(`Attach to process failed for terminal`, shellLaunchConfig.attachPersistentProcess); + shellLaunchConfig.attachPersistentProcess = undefined; } - } else { + } + if (!newProcess) { await this._terminalProfileResolverService.resolveShellLaunchConfig(shellLaunchConfig, { remoteAuthority: this.remoteAuthority, os: this.os @@ -294,10 +296,12 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce if (result) { newProcess = result; } else { - this._logService.trace(`Attach to process failed for terminal ${shellLaunchConfig.attachPersistentProcess}`); - return undefined; + // Warn and just create a new terminal if attach failed for some reason + this._logService.warn(`Attach to process failed for terminal`, shellLaunchConfig.attachPersistentProcess); + shellLaunchConfig.attachPersistentProcess = undefined; } - } else { + } + if (!newProcess) { newProcess = await this._launchLocalProcess(backend, shellLaunchConfig, cols, rows, this.userHome, isScreenReaderModeEnabled, variableResolver); } if (!this._isDisposed) { @@ -335,7 +339,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce if (this._preLaunchInputQueue.length > 0 && this._process) { // Send any queued data that's waiting - newProcess.input(this._preLaunchInputQueue.join('')); + newProcess!.input(this._preLaunchInputQueue.join('')); this._preLaunchInputQueue.length = 0; } }), From b067047af10e46a8502f215181f16bbec060dca4 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 16 Jun 2022 17:34:36 -0700 Subject: [PATCH 123/175] Update src/vs/platform/terminal/node/ptyService.ts Co-authored-by: Megan Rogge --- src/vs/platform/terminal/node/ptyService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index f7014d2a80c..d387f795c72 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -774,7 +774,7 @@ class XtermSerializer implements ITerminalSerializer { } freeRawReviveBuffer(): void { - // Free the memory if the terminal if it will need to be re-serialized + // Free the memory of the terminal if it will need to be re-serialized this._rawReviveBuffer = undefined; } From 5e26d5f9b38543e57de725df779659b9c35c2801 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Thu, 16 Jun 2022 21:59:10 -0400 Subject: [PATCH 124/175] Stop removing app insights in the meantime (#152413) --- build/.moduleignore | 5 ----- build/.webignore | 4 ---- 2 files changed, 9 deletions(-) diff --git a/build/.moduleignore b/build/.moduleignore index 79514212c66..438f9b575d1 100644 --- a/build/.moduleignore +++ b/build/.moduleignore @@ -122,11 +122,6 @@ vscode-windows-ca-certs/**/* node-addon-api/**/* prebuild-install/**/* -@microsoft/applicationinsights*/** -@microsoft/dynamicproto-js/** -!@microsoft/applicationinsights-shims/** -!@microsoft/applicationinsights-web/dist/applicationinsights-web.min.js - # other node modules **/docs/** diff --git a/build/.webignore b/build/.webignore index a47a109b68c..563dfb0000c 100644 --- a/build/.webignore +++ b/build/.webignore @@ -33,7 +33,3 @@ xterm-addon-webgl/out/** # This makes sure the model is included in the package !@vscode/vscode-languagedetection/model/** -@microsoft/applicationinsights*/** -@microsoft/dynamicproto-js/** -!@microsoft/applicationinsights-shims/** -!@microsoft/applicationinsights-web/dist/applicationinsights-web.min.js From 482bc7c14645d4a033cfa440f4e65d65b0d7a4cd Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Thu, 16 Jun 2022 19:13:42 -0700 Subject: [PATCH 125/175] Add experimental Continue Edit Session API command (#152375) * Implement `vscode.experimental.editSession.continue` API command * Read `editSessionId` from protocol url query params Pass it down to `environmentService` for later access Read it from `environmentService` when attempting to apply edit session * Pass `editSessionId` to environmentService in web * Set and clear edit session ID * Add logging and encode ref in query parameters * Update test --- src/vs/code/electron-main/app.ts | 7 ++ .../environment/common/environment.ts | 3 + .../api/common/extHostApiCommands.ts | 6 ++ src/vs/workbench/browser/web.api.ts | 5 ++ .../browser/sessionSync.contribution.ts | 83 ++++++++++++++++--- .../test/browser/sessionSync.test.ts | 3 + .../environment/browser/environmentService.ts | 3 + .../browser/sessionSyncWorkbenchService.ts | 5 +- .../sessionSync/common/sessionSync.ts | 2 +- 9 files changed, 101 insertions(+), 16 deletions(-) diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 8fb281645b9..6db5653c631 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -876,6 +876,13 @@ export class CodeApplication extends Disposable { // or if no window is open (macOS only) shouldOpenInNewWindow ||= isMacintosh && windowsMainService.getWindowCount() === 0; + // Pass along edit session id + if (params.get('edit-session-id') !== null) { + environmentService.editSessionId = params.get('edit-session-id') ?? undefined; + params.delete('edit-session-id'); + uri = uri.with({ query: params.toString() }); + } + // Check for URIs to open in window const windowOpenableFromProtocolLink = app.getWindowOpenableFromProtocolLink(uri); logService.trace('app#handleURL: windowOpenableFromProtocolLink = ', windowOpenableFromProtocolLink); diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 54d4836668a..5ed69a092ef 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -64,6 +64,9 @@ export interface IEnvironmentService { userDataSyncLogResource: URI; sync: 'on' | 'off' | undefined; + // --- continue edit session + editSessionId?: string; + // --- extension development debugExtensionHost: IExtensionHostDebugParams; isExtensionDevelopment: boolean; diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index 4b866a477f3..6b1771703ea 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -436,6 +436,12 @@ const newCommands: ApiCommand[] = [ 'vscode.revealTestInExplorer', '_revealTestInExplorer', 'Reveals a test instance in the explorer', [ApiCommandArgument.TestItem], ApiCommandResult.Void + ), + // --- continue edit session + new ApiCommand( + 'vscode.experimental.editSession.continue', '_workbench.experimental.sessionSync.actions.continueEditSession', 'Continue the current edit session in a different workspace', + [ApiCommandArgument.Uri.with('workspaceUri', 'The target workspace to continue the current edit session in')], + ApiCommandResult.Void ) ]; diff --git a/src/vs/workbench/browser/web.api.ts b/src/vs/workbench/browser/web.api.ts index 4eddbcbba88..99c7b044635 100644 --- a/src/vs/workbench/browser/web.api.ts +++ b/src/vs/workbench/browser/web.api.ts @@ -169,6 +169,11 @@ export interface IWorkbenchConstructionOptions { */ readonly codeExchangeProxyEndpoints?: { [providerId: string]: string }; + /** + * The identifier of an edit session associated with the current workspace. + */ + readonly editSessionId?: string; + /** * [TEMPORARY]: This will be removed soon. * Endpoints to be used for proxying repository tarball download calls in the browser. diff --git a/src/vs/workbench/contrib/sessionSync/browser/sessionSync.contribution.ts b/src/vs/workbench/contrib/sessionSync/browser/sessionSync.contribution.ts index 24aec32b2b6..7ea8636ceae 100644 --- a/src/vs/workbench/contrib/sessionSync/browser/sessionSync.contribution.ts +++ b/src/vs/workbench/contrib/sessionSync/browser/sessionSync.contribution.ts @@ -27,17 +27,24 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { ILogService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; registerSingleton(ISessionSyncWorkbenchService, SessionSyncWorkbenchService); const applyLatestCommand = { - id: 'workbench.sessionSync.actions.applyLatest', + id: 'workbench.experimental.sessionSync.actions.applyLatest', title: localize('apply latest', "{0}: Apply Latest Edit Session", EDIT_SESSION_SYNC_TITLE), }; const storeLatestCommand = { - id: 'workbench.sessionSync.actions.storeLatest', + id: 'workbench.experimental.sessionSync.actions.storeLatest', title: localize('store latest', "{0}: Store Latest Edit Session", EDIT_SESSION_SYNC_TITLE), }; +const continueEditSessionCommand = { + id: '_workbench.experimental.sessionSync.actions.continueEditSession', + title: localize('continue edit session', "{0}: Continue Edit Session", EDIT_SESSION_SYNC_TITLE), +}; +const queryParamName = 'editSessionId'; export class SessionSyncContribution extends Disposable implements IWorkbenchContribution { @@ -47,17 +54,23 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon @ISessionSyncWorkbenchService private readonly sessionSyncWorkbenchService: ISessionSyncWorkbenchService, @IFileService private readonly fileService: IFileService, @IProgressService private readonly progressService: IProgressService, + @IOpenerService private readonly openerService: IOpenerService, @ITelemetryService private readonly telemetryService: ITelemetryService, @ISCMService private readonly scmService: ISCMService, @INotificationService private readonly notificationService: INotificationService, @IDialogService private readonly dialogService: IDialogService, @ILogService private readonly logService: ILogService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, @IProductService private readonly productService: IProductService, @IConfigurationService private configurationService: IConfigurationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, ) { super(); + if (this.environmentService.editSessionId !== undefined) { + void this.applyEditSession(this.environmentService.editSessionId).then(() => this.environmentService.editSessionId = undefined); + } + this.configurationService.onDidChangeConfiguration((e) => { if (e.affectsConfiguration('workbench.experimental.sessionSync.enabled')) { this.registerActions(); @@ -72,15 +85,50 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon return; } - this.registerApplyEditSessionAction(); - this.registerStoreEditSessionAction(); + this.registerContinueEditSessionAction(); + + this.registerApplyLatestEditSessionAction(); + this.registerStoreLatestEditSessionAction(); this.registered = true; } - private registerApplyEditSessionAction(): void { + private registerContinueEditSessionAction() { const that = this; - this._register(registerAction2(class ApplyEditSessionAction extends Action2 { + this._register(registerAction2(class ContinueEditSessionAction extends Action2 { + constructor() { + super({ + id: continueEditSessionCommand.id, + title: continueEditSessionCommand.title + }); + } + + async run(accessor: ServicesAccessor, workspaceUri: URI): Promise { + // Run the store action to get back a ref + const ref = await that.storeEditSession(); + + // Append the ref to the URI + if (ref !== undefined) { + const encodedRef = encodeURIComponent(ref); + workspaceUri = workspaceUri.with({ + query: workspaceUri.query.length > 0 ? (workspaceUri + `&${queryParamName}=${encodedRef}`) : `${queryParamName}=${encodedRef}` + }); + + that.environmentService.editSessionId = ref; + } else { + that.logService.warn(`Edit Sessions: Failed to store edit session when invoking ${continueEditSessionCommand.id}.`); + } + + // Open the URI + that.logService.info(`Edit Sessions: opening ${workspaceUri.toString()}`); + await that.openerService.open(workspaceUri, { openExternal: true }); + } + })); + } + + private registerApplyLatestEditSessionAction(): void { + const that = this; + this._register(registerAction2(class ApplyLatestEditSessionAction extends Action2 { constructor() { super({ id: applyLatestCommand.id, @@ -100,9 +148,9 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon })); } - private registerStoreEditSessionAction(): void { + private registerStoreLatestEditSessionAction(): void { const that = this; - this._register(registerAction2(class StoreEditSessionAction extends Action2 { + this._register(registerAction2(class StoreLatestEditSessionAction extends Action2 { constructor() { super({ id: storeLatestCommand.id, @@ -122,8 +170,12 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon })); } - async applyEditSession() { - const editSession = await this.sessionSyncWorkbenchService.read(undefined); + async applyEditSession(ref?: string): Promise { + if (ref !== undefined) { + this.logService.info(`Edit Sessions: Applying edit session with ref ${ref}.`); + } + + const editSession = await this.sessionSyncWorkbenchService.read(ref); if (!editSession) { return; } @@ -160,6 +212,7 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon } if (hasLocalUncommittedChanges) { + // TODO@joyceerhl Provide the option to diff files which would be overwritten by edit session contents const result = await this.dialogService.confirm({ message: localize('apply edit session warning', 'Applying your edit session may overwrite your existing uncommitted changes. Do you want to proceed?'), type: 'warning', @@ -178,12 +231,12 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon } } } catch (ex) { - this.logService.error(ex); + this.logService.error('Edit Sessions:', (ex as Error).toString()); this.notificationService.error(localize('apply failed', "Failed to apply your edit session.")); } } - async storeEditSession() { + async storeEditSession(): Promise { const folders: Folder[] = []; for (const repository of this.scmService.repositories) { @@ -223,7 +276,9 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon const data: EditSession = { folders, version: 1 }; try { - await this.sessionSyncWorkbenchService.write(data); + const ref = await this.sessionSyncWorkbenchService.write(data); + this.logService.info(`Edit Sessions: Stored edit session with ref ${ref}.`); + return ref; } catch (ex) { type UploadFailedEvent = { reason: string }; type UploadFailedClassification = { @@ -245,6 +300,8 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon } } } + + return undefined; } private getChangedResources(repository: ISCMRepository) { diff --git a/src/vs/workbench/contrib/sessionSync/test/browser/sessionSync.test.ts b/src/vs/workbench/contrib/sessionSync/test/browser/sessionSync.test.ts index b4f169a12f9..83cfaf3e64a 100644 --- a/src/vs/workbench/contrib/sessionSync/test/browser/sessionSync.test.ts +++ b/src/vs/workbench/contrib/sessionSync/test/browser/sessionSync.test.ts @@ -26,6 +26,8 @@ import { URI } from 'vs/base/common/uri'; import { joinPath } from 'vs/base/common/resources'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; +import { TestEnvironmentService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; const folderName = 'test-folder'; const folderUri = URI.file(`/${folderName}`); @@ -53,6 +55,7 @@ suite('Edit session sync', () => { instantiationService.stub(ISessionSyncWorkbenchService, new class extends mock() { }); instantiationService.stub(IProgressService, ProgressService); instantiationService.stub(ISCMService, SCMService); + instantiationService.stub(IEnvironmentService, TestEnvironmentService); instantiationService.stub(IConfigurationService, new TestConfigurationService({ workbench: { experimental: { sessionSync: { enabled: true } } } })); instantiationService.stub(IWorkspaceContextService, new class extends mock() { override getWorkspace() { diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index 1efe771ec2c..adbb3c495c8 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -205,6 +205,9 @@ export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvi @memoize get disableWorkspaceTrust(): boolean { return !this.options.enableWorkspaceTrust; } + @memoize + get editSessionId(): string | undefined { return this.options.editSessionId; } + private payload: Map | undefined; constructor( diff --git a/src/vs/workbench/services/sessionSync/browser/sessionSyncWorkbenchService.ts b/src/vs/workbench/services/sessionSync/browser/sessionSyncWorkbenchService.ts index 894141cce99..68a8dd657bb 100644 --- a/src/vs/workbench/services/sessionSync/browser/sessionSyncWorkbenchService.ts +++ b/src/vs/workbench/services/sessionSync/browser/sessionSyncWorkbenchService.ts @@ -60,14 +60,15 @@ export class SessionSyncWorkbenchService extends Disposable implements ISessionS /** * * @param editSession An object representing edit session state to be restored. + * @returns The ref of the stored edit session state. */ - async write(editSession: EditSession): Promise { + async write(editSession: EditSession): Promise { this.initialized = await this.waitAndInitialize(); if (!this.initialized) { throw new Error('Please sign in to store your edit session.'); } - await this.storeClient?.write('editSessions', JSON.stringify(editSession), null); + return this.storeClient!.write('editSessions', JSON.stringify(editSession), null); } /** diff --git a/src/vs/workbench/services/sessionSync/common/sessionSync.ts b/src/vs/workbench/services/sessionSync/common/sessionSync.ts index 4b91a77f95e..7e4af5123ec 100644 --- a/src/vs/workbench/services/sessionSync/common/sessionSync.ts +++ b/src/vs/workbench/services/sessionSync/common/sessionSync.ts @@ -13,7 +13,7 @@ export interface ISessionSyncWorkbenchService { _serviceBrand: undefined; read(ref: string | undefined): Promise; - write(editSession: EditSession): Promise; + write(editSession: EditSession): Promise; } export enum ChangeType { From 2f4334e07c30b230ab6015c242d2476099ebf364 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Thu, 16 Jun 2022 20:13:28 -0700 Subject: [PATCH 126/175] fix build: try not using utility proc by default (#152416) try not using utility proc by default --- .../contrib/extensions/browser/extensions.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 6fbc52d18ba..befa640deb2 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -227,7 +227,7 @@ Registry.as(ConfigurationExtensions.Configuration) 'extensions.experimental.useUtilityProcess': { type: 'boolean', description: localize('extensionsUseUtilityProcess', "When enabled, the extension host will be launched using the new UtilityProcess Electron API."), - default: true + default: false }, [WORKSPACE_TRUST_EXTENSION_SUPPORT]: { type: 'object', From fbf1cf3832d43088e27837dbb68d24ab65a098c1 Mon Sep 17 00:00:00 2001 From: Robo Date: Fri, 17 Jun 2022 13:35:01 +0900 Subject: [PATCH 127/175] chore: update electron@18.3.3 (#152410) Bumps microsoft-build from 14009868 -> 14029392 From 47f417529cd8492bd3d5d76488e78c8652da9a54 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 17 Jun 2022 08:20:16 +0200 Subject: [PATCH 128/175] Revert "fix smoke tests on linux (#152232)" (#152418) This reverts commit 5f696f9955d0fbf855a336342ddd8ce5a90ad8fc. --- .../linux/product-build-linux-client-test.yml | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/build/azure-pipelines/linux/product-build-linux-client-test.yml b/build/azure-pipelines/linux/product-build-linux-client-test.yml index 9627b1e8b93..48985fa539d 100644 --- a/build/azure-pipelines/linux/product-build-linux-client-test.yml +++ b/build/azure-pipelines/linux/product-build-linux-client-test.yml @@ -122,24 +122,23 @@ steps: timeoutInMinutes: 20 displayName: Run smoke tests (Browser, Chromium) - # TODO enable again after https://github.com/microsoft/vscode/issues/152143 is fixed - # - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: - # - script: | - # set -e - # APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - # yarn smoketest-no-compile --tracing --build "$APP_PATH" - # timeoutInMinutes: 20 - # displayName: Run smoke tests (Electron) + - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: + - script: | + set -e + APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) + yarn smoketest-no-compile --tracing --build "$APP_PATH" + timeoutInMinutes: 20 + displayName: Run smoke tests (Electron) - # - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: - # - script: | - # set -e - # yarn gulp compile-extension:vscode-test-resolver - # APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - # VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ - # yarn smoketest-no-compile --tracing --remote --build "$APP_PATH" - # timeoutInMinutes: 20 - # displayName: Run smoke tests (Remote) + - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: + - script: | + set -e + yarn gulp compile-extension:vscode-test-resolver + APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ + yarn smoketest-no-compile --tracing --remote --build "$APP_PATH" + timeoutInMinutes: 20 + displayName: Run smoke tests (Remote) - ${{ if eq(parameters.VSCODE_RUN_SMOKE_TESTS, true) }}: - script: | From 761f8b21dfea3d28d386fd1f7e007bc85ef7383b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 17 Jun 2022 08:49:57 +0200 Subject: [PATCH 129/175] storage - allow to migrate to a new profile (#152317) * storage - allow to migrate to a new profile * reuse data migration code * use correct profile for logging * cleanup * address feedback * dont dipsose store * lipstick * align * simplify * rensmes * pause emitters * reuse --- .../find/test/browser/findController.test.ts | 8 +- .../test/browser/multicursor.test.ts | 4 +- .../storage/browser/storageService.ts | 144 ++++++++++++------ src/vs/platform/storage/common/storage.ts | 64 +++++++- src/vs/platform/storage/common/storageIpc.ts | 46 +----- .../electron-main/storageMainService.ts | 14 +- .../electron-sandbox/storageService.ts | 107 +++++++------ .../userDataProfile/common/userDataProfile.ts | 19 ++- .../browser/actions/developerActions.ts | 2 +- .../userDataProfile/common/profileActions.ts | 6 +- .../common/userDataProfileActions.ts | 6 +- .../userDataProfile/common/userDataProfile.ts | 2 +- .../workspaceEditingService.ts | 2 +- 13 files changed, 264 insertions(+), 160 deletions(-) diff --git a/src/vs/editor/contrib/find/test/browser/findController.test.ts b/src/vs/editor/contrib/find/test/browser/findController.test.ts index 7bcba36df5c..e3c97f93cd2 100644 --- a/src/vs/editor/contrib/find/test/browser/findController.test.ts +++ b/src/vs/editor/contrib/find/test/browser/findController.test.ts @@ -80,8 +80,8 @@ suite('FindController', async () => { isNew: () => false, flush: () => { return Promise.resolve(); }, keys: () => [], - logStorage: () => { }, - migrate: () => { throw new Error(); } + log: () => { }, + switch: () => { throw new Error(); } } as IStorageService); if (platform.isMacintosh) { @@ -511,8 +511,8 @@ suite('FindController query options persistence', async () => { isNew: () => false, flush: () => { return Promise.resolve(); }, keys: () => [], - logStorage: () => { }, - migrate: () => { throw new Error(); } + log: () => { }, + switch: () => { throw new Error(); } } as IStorageService); test('matchCase', async () => { diff --git a/src/vs/editor/contrib/multicursor/test/browser/multicursor.test.ts b/src/vs/editor/contrib/multicursor/test/browser/multicursor.test.ts index 224ce76ce3a..70f5e5a648d 100644 --- a/src/vs/editor/contrib/multicursor/test/browser/multicursor.test.ts +++ b/src/vs/editor/contrib/multicursor/test/browser/multicursor.test.ts @@ -94,8 +94,8 @@ suite('Multicursor selection', () => { getNumber: (key: string) => undefined!, store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); }, remove: (key) => undefined, - logStorage: () => undefined, - migrate: (toWorkspace) => Promise.resolve(undefined), + log: () => undefined, + switch: () => Promise.resolve(undefined), flush: () => Promise.resolve(undefined), isNew: () => true, keys: () => [] diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts index 63b7cf9e7b2..219aed37b5a 100644 --- a/src/vs/platform/storage/browser/storageService.ts +++ b/src/vs/platform/storage/browser/storageService.ts @@ -5,14 +5,15 @@ import { isSafari } from 'vs/base/browser/browser'; import { IndexedDB } from 'vs/base/browser/indexedDB'; -import { Promises } from 'vs/base/common/async'; +import { DeferredPromise, Promises } from 'vs/base/common/async'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Emitter } from 'vs/base/common/event'; -import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { assertIsDefined } from 'vs/base/common/types'; import { InMemoryStorageDatabase, isStorageItemsChangeEvent, IStorage, IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest, Storage } from 'vs/base/parts/storage/common/storage'; import { ILogService } from 'vs/platform/log/common/log'; import { AbstractStorageService, IS_NEW_KEY, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IAnyWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export class BrowserStorageService extends AbstractStorageService { @@ -20,11 +21,15 @@ export class BrowserStorageService extends AbstractStorageService { private static BROWSER_DEFAULT_FLUSH_INTERVAL = 5 * 1000; // every 5s because async operations are not permitted on shutdown private applicationStorage: IStorage | undefined; - private globalStorage: IStorage | undefined; - private workspaceStorage: IStorage | undefined; - private applicationStorageDatabase: IIndexedDBStorageDatabase | undefined; + private readonly applicationStoragePromise = new DeferredPromise<{ indededDb: IIndexedDBStorageDatabase; storage: IStorage }>(); + + private globalStorage: IStorage | undefined; private globalStorageDatabase: IIndexedDBStorageDatabase | undefined; + private globalStorageProfile: IUserDataProfile; + private readonly globalStorageDisposables = this._register(new DisposableStore()); + + private workspaceStorage: IStorage | undefined; private workspaceStorageDatabase: IIndexedDBStorageDatabase | undefined; get hasPendingUpdate(): boolean { @@ -38,9 +43,11 @@ export class BrowserStorageService extends AbstractStorageService { constructor( private readonly payload: IAnyWorkspaceIdentifier, @ILogService private readonly logService: ILogService, - @IUserDataProfilesService private readonly userDataProfileService: IUserDataProfilesService + @IUserDataProfilesService userDataProfileService: IUserDataProfilesService ) { super({ flushInterval: BrowserStorageService.BROWSER_DEFAULT_FLUSH_INTERVAL }); + + this.globalStorageProfile = userDataProfileService.currentProfile; } private getId(scope: StorageScope): string { @@ -48,10 +55,10 @@ export class BrowserStorageService extends AbstractStorageService { case StorageScope.APPLICATION: return 'global'; // use the default profile global DB for application scope case StorageScope.GLOBAL: - if (this.userDataProfileService.currentProfile.isDefault) { + if (this.globalStorageProfile.isDefault) { return 'global'; // default profile DB has a fixed name for backwards compatibility } else { - return `global-${this.userDataProfileService.currentProfile.id}`; + return `global-${this.globalStorageProfile.id}`; } case StorageScope.WORKSPACE: return this.payload.id; @@ -60,55 +67,81 @@ export class BrowserStorageService extends AbstractStorageService { protected async doInitialize(): Promise { - // Create Storage in Parallel - const promises: Promise[] = []; - promises.push(IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.APPLICATION), broadcastChanges: true }, this.logService)); - promises.push(IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.WORKSPACE) }, this.logService)); - if (!this.userDataProfileService.currentProfile.isDefault) { + // Init storages + await Promises.settled([ + this.createApplicationStorage(), + this.createGlobalStorage(this.globalStorageProfile), + this.createWorkspaceStorage() + ]); + } + + private async createApplicationStorage(): Promise { + const applicationStorageIndexedDB = await IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.APPLICATION), broadcastChanges: true }, this.logService); + + this.applicationStorageDatabase = this._register(applicationStorageIndexedDB); + this.applicationStorage = this._register(new Storage(this.applicationStorageDatabase)); + + this._register(this.applicationStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.APPLICATION, key))); + + await this.applicationStorage.init(); + + this.updateIsNew(this.applicationStorage); + + this.applicationStoragePromise.complete({ indededDb: applicationStorageIndexedDB, storage: this.applicationStorage }); + } + + private async createGlobalStorage(profile: IUserDataProfile): Promise { + + // First clear any previously associated disposables + this.globalStorageDisposables.clear(); + + // Remember profile associated to global storage + this.globalStorageProfile = profile; + + if (this.globalStorageProfile.isDefault) { // If we are in default profile, the global storage is // actually the same as application storage. As such we // avoid creating the storage library a second time on // the same DB. - promises.push(IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.GLOBAL), broadcastChanges: true }, this.logService)); - } - const [applicationStorageDatabase, workspaceStorageDatabase, globalStorageDatabase] = await Promises.settled(promises); + const { indededDb: applicationStorageIndexedDB, storage: applicationStorage } = await this.applicationStoragePromise.p; - // Workspace Storage - this.workspaceStorageDatabase = this._register(workspaceStorageDatabase); + this.globalStorageDatabase = applicationStorageIndexedDB; + this.globalStorage = applicationStorage; + } else { + const globalStorageIndexedDB = await IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.GLOBAL), broadcastChanges: true }, this.logService); + + this.globalStorageDatabase = this.globalStorageDisposables.add(globalStorageIndexedDB); + this.globalStorage = this.globalStorageDisposables.add(new Storage(this.globalStorageDatabase)); + } + + this.globalStorageDisposables.add(this.globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); + + await this.globalStorage.init(); + + this.updateIsNew(this.globalStorage); + } + + private async createWorkspaceStorage(): Promise { + const workspaceStorageIndexedDB = await IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.WORKSPACE) }, this.logService); + + this.workspaceStorageDatabase = this._register(workspaceStorageIndexedDB); this.workspaceStorage = this._register(new Storage(this.workspaceStorageDatabase)); + this._register(this.workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key))); - // Application Storage - this.applicationStorageDatabase = this._register(applicationStorageDatabase); - this.applicationStorage = this._register(new Storage(this.applicationStorageDatabase)); - this._register(this.applicationStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.APPLICATION, key))); + await this.workspaceStorage.init(); - // Global Storage - if (globalStorageDatabase) { - this.globalStorageDatabase = this._register(globalStorageDatabase); - this.globalStorage = this._register(new Storage(this.globalStorageDatabase)); - } else { - this.globalStorage = this.applicationStorage; - } - this._register(this.globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); + this.updateIsNew(this.workspaceStorage); + } - // Init storages - await Promises.settled([ - this.workspaceStorage.init(), - this.globalStorage.init(), - this.applicationStorage.init() - ]); - - // Apply is-new markers - for (const storage of [this.applicationStorage, this.globalStorage, this.workspaceStorage]) { - const firstOpen = storage.getBoolean(IS_NEW_KEY); - if (firstOpen === undefined) { - storage.set(IS_NEW_KEY, true); - } else if (firstOpen) { - storage.set(IS_NEW_KEY, false); - } + private updateIsNew(storage: IStorage): void { + const firstOpen = storage.getBoolean(IS_NEW_KEY); + if (firstOpen === undefined) { + storage.set(IS_NEW_KEY, true); + } else if (firstOpen) { + storage.set(IS_NEW_KEY, false); } } @@ -127,7 +160,24 @@ export class BrowserStorageService extends AbstractStorageService { return this.getId(scope); } - async migrate(toWorkspace: IAnyWorkspaceIdentifier): Promise { + protected async switchToProfile(toProfile: IUserDataProfile, preserveData: boolean): Promise { + const oldGlobalStorage = assertIsDefined(this.globalStorage); + const oldItems = oldGlobalStorage.items; + + // Close old global storage but only if this is + // different from application storage! + if (oldGlobalStorage !== this.applicationStorage) { + await oldGlobalStorage.close(); + } + + // Create new global storage & init + await this.createGlobalStorage(toProfile); + + // Handle data switch and eventing + this.switchData(oldItems, assertIsDefined(this.globalStorage), StorageScope.GLOBAL, preserveData); + } + + protected async switchToWorkspace(toWorkspace: IAnyWorkspaceIdentifier, preserveData: boolean): Promise { throw new Error('Migrating storage is currently unsupported in Web'); } diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts index 9061792183d..a3d8fd7e3b1 100644 --- a/src/vs/platform/storage/common/storage.ts +++ b/src/vs/platform/storage/common/storage.ts @@ -10,6 +10,7 @@ import { mark } from 'vs/base/common/performance'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { InMemoryStorageDatabase, IStorage, Storage } from 'vs/base/parts/storage/common/storage'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { isUserDataProfile, IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IAnyWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export const IS_NEW_KEY = '__$__isNewStorageMarker'; @@ -137,12 +138,13 @@ export interface IStorageService { /** * Log the contents of the storage to the console. */ - logStorage(): void; + log(): void; /** - * Migrate the storage contents to another workspace. + * Switch storage to another workspace or profile. Optionally preserve the + * current data to the new storage. */ - migrate(toWorkspace: IAnyWorkspaceIdentifier): Promise; + switch(to: IAnyWorkspaceIdentifier | IUserDataProfile, preserveData: boolean): Promise; /** * Whether the storage for the given scope was created during this session or @@ -521,7 +523,7 @@ export abstract class AbstractStorageService extends Disposable implements IStor } } - async logStorage(): Promise { + async log(): Promise { const applicationItems = this.getStorage(StorageScope.APPLICATION)?.items ?? new Map(); const globalItems = this.getStorage(StorageScope.GLOBAL)?.items ?? new Map(); const workspaceItems = this.getStorage(StorageScope.WORKSPACE)?.items ?? new Map(); @@ -536,6 +538,49 @@ export abstract class AbstractStorageService extends Disposable implements IStor ); } + async switch(to: IAnyWorkspaceIdentifier | IUserDataProfile, preserveData: boolean): Promise { + + // Signal as event so that clients can store data before we switch + this.emitWillSaveState(WillSaveStateReason.NONE); + + if (isUserDataProfile(to)) { + return this.switchToProfile(to, preserveData); + } + + return this.switchToWorkspace(to, preserveData); + } + + protected switchData(oldStorage: Map, newStorage: IStorage, scope: StorageScope, preserveData: boolean): void { + this.withPausedEmitters(() => { + + // Copy over previous keys if `preserveData` + if (preserveData) { + for (const [key, value] of oldStorage) { + newStorage.set(key, value); + } + } + + // Otherwise signal storage keys that have changed + else { + const handledkeys = new Set(); + for (const [key, oldValue] of oldStorage) { + handledkeys.add(key); + + const newValue = newStorage.get(key); + if (newValue !== oldValue) { + this.emitDidChangeValue(scope, key); + } + } + + for (const [key] of newStorage.items) { + if (!handledkeys.has(key)) { + this.emitDidChangeValue(scope, key); + } + } + } + }); + } + // --- abstract protected abstract doInitialize(): Promise; @@ -544,7 +589,8 @@ export abstract class AbstractStorageService extends Disposable implements IStor protected abstract getLogDetails(scope: StorageScope): string | undefined; - abstract migrate(toWorkspace: IAnyWorkspaceIdentifier): Promise; + protected abstract switchToProfile(toProfile: IUserDataProfile, preserveData: boolean): Promise; + protected abstract switchToWorkspace(toWorkspace: IAnyWorkspaceIdentifier | IUserDataProfile, preserveData: boolean): Promise; } export class InMemoryStorageService extends AbstractStorageService { @@ -585,8 +631,12 @@ export class InMemoryStorageService extends AbstractStorageService { protected async doInitialize(): Promise { } - async migrate(toWorkspace: IAnyWorkspaceIdentifier): Promise { - // not supported + protected async switchToProfile(): Promise { + // no-op when in-memory + } + + protected async switchToWorkspace(): Promise { + // no-op when in-memory } } diff --git a/src/vs/platform/storage/common/storageIpc.ts b/src/vs/platform/storage/common/storageIpc.ts index 90572b900ea..240824ad37b 100644 --- a/src/vs/platform/storage/common/storageIpc.ts +++ b/src/vs/platform/storage/common/storageIpc.ts @@ -8,7 +8,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { UriDto } from 'vs/base/common/types'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/base/parts/storage/common/storage'; -import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { ISerializedSingleFolderWorkspaceIdentifier, ISerializedWorkspaceIdentifier, IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export type Key = string; @@ -102,7 +102,7 @@ abstract class BaseProfileAwareStorageDatabaseClient extends BaseStorageDatabase } } -class ApplicationStorageDatabaseClient extends BaseProfileAwareStorageDatabaseClient { +export class ApplicationStorageDatabaseClient extends BaseProfileAwareStorageDatabaseClient { constructor(channel: IChannel) { super(channel, undefined); @@ -118,7 +118,7 @@ class ApplicationStorageDatabaseClient extends BaseProfileAwareStorageDatabaseCl } } -class GlobalStorageDatabaseClient extends BaseProfileAwareStorageDatabaseClient { +export class GlobalStorageDatabaseClient extends BaseProfileAwareStorageDatabaseClient { constructor(channel: IChannel, profile: UriDto) { super(channel, profile); @@ -135,7 +135,7 @@ class GlobalStorageDatabaseClient extends BaseProfileAwareStorageDatabaseClient } } -class WorkspaceStorageDatabaseClient extends BaseStorageDatabaseClient implements IStorageDatabase { +export class WorkspaceStorageDatabaseClient extends BaseStorageDatabaseClient implements IStorageDatabase { readonly onDidChangeItemsExternal = Event.None; // unsupported for workspace storage because we only ever write from one window @@ -152,41 +152,3 @@ class WorkspaceStorageDatabaseClient extends BaseStorageDatabaseClient implement this.dispose(); } } - -export class StorageDatabaseChannelClient extends Disposable { - - private _applicationStorage: ApplicationStorageDatabaseClient | undefined = undefined; - get applicationStorage() { - if (!this._applicationStorage) { - this._applicationStorage = new ApplicationStorageDatabaseClient(this.channel); - } - - return this._applicationStorage; - } - - private _globalStorage: GlobalStorageDatabaseClient | undefined = undefined; - get globalStorage() { - if (!this._globalStorage) { - this._globalStorage = new GlobalStorageDatabaseClient(this.channel, this.userDataProfileService.currentProfile); - } - - return this._globalStorage; - } - - private _workspaceStorage: WorkspaceStorageDatabaseClient | undefined = undefined; - get workspaceStorage() { - if (!this._workspaceStorage && this.workspace) { - this._workspaceStorage = new WorkspaceStorageDatabaseClient(this.channel, this.workspace); - } - - return this._workspaceStorage; - } - - constructor( - private channel: IChannel, - private userDataProfileService: IUserDataProfilesService, - private workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined - ) { - super(); - } -} diff --git a/src/vs/platform/storage/electron-main/storageMainService.ts b/src/vs/platform/storage/electron-main/storageMainService.ts index e1770742e7e..0fa9144e1cd 100644 --- a/src/vs/platform/storage/electron-main/storageMainService.ts +++ b/src/vs/platform/storage/electron-main/storageMainService.ts @@ -14,7 +14,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { AbstractStorageService, IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ApplicationStorageMain, GlobalStorageMain, InMemoryStorageMain, IStorageMain, IStorageMainOptions, WorkspaceStorageMain } from 'vs/platform/storage/electron-main/storageMain'; import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; -import { IAnyWorkspaceIdentifier, IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; +import { IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; //#region Storage Main Service (intent: make application, global and workspace storage accessible to windows from main process) @@ -258,7 +258,7 @@ export interface IApplicationStorageMainService extends IStorageService { keys(scope: StorageScope.APPLICATION, target: StorageTarget): string[]; - migrate(toWorkspace: IAnyWorkspaceIdentifier): never; + switch(): never; isNew(scope: StorageScope.APPLICATION): boolean; } @@ -304,7 +304,15 @@ export class ApplicationStorageMainService extends AbstractStorageService implem return false; // not needed here, will be triggered from any window that is opened } - migrate(): never { + override switch(): never { throw new Error('Migrating storage is unsupported from main process'); } + + protected switchToProfile(): never { + throw new Error('Switching storage profile is unsupported from main process'); + } + + protected switchToWorkspace(): never { + throw new Error('Switching storage workspace is unsupported from main process'); + } } diff --git a/src/vs/platform/storage/electron-sandbox/storageService.ts b/src/vs/platform/storage/electron-sandbox/storageService.ts index 893dfbcb552..cd8274acd5c 100644 --- a/src/vs/platform/storage/electron-sandbox/storageService.ts +++ b/src/vs/platform/storage/electron-sandbox/storageService.ts @@ -4,58 +4,63 @@ *--------------------------------------------------------------------------------------------*/ import { Promises } from 'vs/base/common/async'; -import { MutableDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { joinPath } from 'vs/base/common/resources'; import { IStorage, Storage } from 'vs/base/parts/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services'; import { AbstractStorageService, StorageScope, WillSaveStateReason } from 'vs/platform/storage/common/storage'; -import { StorageDatabaseChannelClient } from 'vs/platform/storage/common/storageIpc'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { ApplicationStorageDatabaseClient, GlobalStorageDatabaseClient, WorkspaceStorageDatabaseClient } from 'vs/platform/storage/common/storageIpc'; +import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IAnyWorkspaceIdentifier, IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export class NativeStorageService extends AbstractStorageService { - // Application Storage is readonly and shared across - // windows and profiles. private readonly applicationStorage: IStorage; + private readonly applicationStorageProfile: IUserDataProfile; - // Global Storage is readonly and shared across windows - // under the same profile. - private readonly globalStorage: IStorage; + private globalStorage: IStorage; + private globalStorageProfile: IUserDataProfile | undefined = undefined; + private readonly globalStorageDisposables = this._register(new DisposableStore()); - // Workspace Storage is scoped to a window but can change - // in the current window, when entering a workspace! private workspaceStorage: IStorage | undefined = undefined; private workspaceStorageId: string | undefined = undefined; - private workspaceStorageDisposable = this._register(new MutableDisposable()); + private readonly workspaceStorageDisposables = this._register(new DisposableStore()); constructor( workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined, private readonly mainProcessService: IMainProcessService, - private readonly userDataProfilesService: IUserDataProfilesService, - private readonly environmentService: IEnvironmentService, + userDataProfilesService: IUserDataProfilesService, + private readonly environmentService: IEnvironmentService ) { super(); + this.applicationStorageProfile = userDataProfilesService.defaultProfile; + this.applicationStorage = this.createApplicationStorage(); - this.globalStorage = this.createGlobalStorage(); + this.globalStorage = this.createGlobalStorage(userDataProfilesService.currentProfile); this.workspaceStorage = this.createWorkspaceStorage(workspace); } private createApplicationStorage(): IStorage { - const storageDataBaseClient = new StorageDatabaseChannelClient(this.mainProcessService.getChannel('storage'), this.userDataProfilesService, undefined); - const applicationStorage = new Storage(storageDataBaseClient.applicationStorage); + const storageDataBaseClient = this._register(new ApplicationStorageDatabaseClient(this.mainProcessService.getChannel('storage'))); + const applicationStorage = this._register(new Storage(storageDataBaseClient)); this._register(applicationStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.APPLICATION, key))); return applicationStorage; } - private createGlobalStorage(): IStorage { - let globalStorage: IStorage; + private createGlobalStorage(profile: IUserDataProfile): IStorage { - if (this.userDataProfilesService.currentProfile.isDefault) { + // First clear any previously associated disposables + this.globalStorageDisposables.clear(); + + // Remember profile associated to global storage + this.globalStorageProfile = profile; + + let globalStorage: IStorage; + if (profile.isDefault) { // If we are in default profile, the global storage is // actually the same as application storage. As such we @@ -64,11 +69,11 @@ export class NativeStorageService extends AbstractStorageService { globalStorage = this.applicationStorage; } else { - const storageDataBaseClient = new StorageDatabaseChannelClient(this.mainProcessService.getChannel('storage'), this.userDataProfilesService, undefined); - globalStorage = new Storage(storageDataBaseClient.globalStorage); + const storageDataBaseClient = this.globalStorageDisposables.add(new GlobalStorageDatabaseClient(this.mainProcessService.getChannel('storage'), profile)); + globalStorage = this.globalStorageDisposables.add(new Storage(storageDataBaseClient)); } - this._register(globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); + this.globalStorageDisposables.add(globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); return globalStorage; } @@ -76,21 +81,22 @@ export class NativeStorageService extends AbstractStorageService { private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier): IStorage; private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined): IStorage | undefined; private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined): IStorage | undefined { - const storageDataBaseClient = new StorageDatabaseChannelClient(this.mainProcessService.getChannel('storage'), this.userDataProfilesService, workspace); - if (storageDataBaseClient.workspaceStorage) { - const workspaceStorage = new Storage(storageDataBaseClient.workspaceStorage); + // First clear any previously associated disposables + this.workspaceStorageDisposables.clear(); - this.workspaceStorageDisposable.value = workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key)); - this.workspaceStorageId = workspace?.id; + // Remember workspace ID for logging later + this.workspaceStorageId = workspace?.id; - return workspaceStorage; - } else { - this.workspaceStorageDisposable.clear(); - this.workspaceStorageId = undefined; + let workspaceStorage: IStorage | undefined = undefined; + if (workspace) { + const storageDataBaseClient = this.workspaceStorageDisposables.add(new WorkspaceStorageDatabaseClient(this.mainProcessService.getChannel('storage'), workspace)); + workspaceStorage = this.workspaceStorageDisposables.add(new Storage(storageDataBaseClient)); - return undefined; + this.workspaceStorageDisposables.add(workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key))); } + + return workspaceStorage; } protected async doInitialize(): Promise { @@ -117,9 +123,9 @@ export class NativeStorageService extends AbstractStorageService { protected getLogDetails(scope: StorageScope): string | undefined { switch (scope) { case StorageScope.APPLICATION: - return this.userDataProfilesService.defaultProfile.globalStorageHome.fsPath; + return this.applicationStorageProfile.globalStorageHome.fsPath; case StorageScope.GLOBAL: - return this.userDataProfilesService.currentProfile.globalStorageHome.fsPath; + return this.globalStorageProfile?.globalStorageHome.fsPath; default: return this.workspaceStorageId ? `${joinPath(this.environmentService.workspaceStorageHome, this.workspaceStorageId, 'state.vscdb').fsPath}` : undefined; } @@ -141,25 +147,36 @@ export class NativeStorageService extends AbstractStorageService { ]); } - async migrate(toWorkspace: IAnyWorkspaceIdentifier): Promise { + protected async switchToProfile(toProfile: IUserDataProfile, preserveData: boolean): Promise { + const oldGlobalStorage = this.globalStorage; + const oldItems = oldGlobalStorage.items; - // Keep current workspace storage items around to restore + // Close old global storage but only if this is + // different from application storage! + if (oldGlobalStorage !== this.applicationStorage) { + await oldGlobalStorage.close(); + } + + // Create new global storage & init + this.globalStorage = this.createGlobalStorage(toProfile); + await this.globalStorage.init(); + + // Handle data switch and eventing + this.switchData(oldItems, this.globalStorage, StorageScope.GLOBAL, preserveData); + } + + protected async switchToWorkspace(toWorkspace: IAnyWorkspaceIdentifier, preserveData: boolean): Promise { const oldWorkspaceStorage = this.workspaceStorage; const oldItems = oldWorkspaceStorage?.items ?? new Map(); - // Close current which will change to new workspace storage - if (oldWorkspaceStorage) { - await oldWorkspaceStorage.close(); - oldWorkspaceStorage.dispose(); - } + // Close old workspace storage + await oldWorkspaceStorage?.close(); // Create new workspace storage & init this.workspaceStorage = this.createWorkspaceStorage(toWorkspace); await this.workspaceStorage.init(); - // Copy over previous keys - for (const [key, value] of oldItems) { - this.workspaceStorage.set(key, value); - } + // Handle data switch and eventing + this.switchData(oldItems, this.workspaceStorage, StorageScope.WORKSPACE, preserveData); } } diff --git a/src/vs/platform/userDataProfile/common/userDataProfile.ts b/src/vs/platform/userDataProfile/common/userDataProfile.ts index 5db34ee2f99..d6629f8c0c5 100644 --- a/src/vs/platform/userDataProfile/common/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/common/userDataProfile.ts @@ -7,7 +7,7 @@ import { hash } from 'vs/base/common/hash'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { joinPath } from 'vs/base/common/resources'; -import { UriDto } from 'vs/base/common/types'; +import { isUndefined, UriDto } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -47,6 +47,23 @@ export interface IUserDataProfile { readonly extensionsResource: URI | undefined; } +export function isUserDataProfile(thing: unknown): thing is IUserDataProfile { + const candidate = thing as IUserDataProfile | undefined; + + return !!(candidate && typeof candidate === 'object' + && typeof candidate.id === 'string' + && typeof candidate.isDefault === 'boolean' + && typeof candidate.name === 'string' + && URI.isUri(candidate.location) + && URI.isUri(candidate.globalStorageHome) + && URI.isUri(candidate.settingsResource) + && URI.isUri(candidate.keybindingsResource) + && URI.isUri(candidate.tasksResource) + && URI.isUri(candidate.snippetsHome) + && (isUndefined(candidate.extensionsResource) || URI.isUri(candidate.extensionsResource)) + ); +} + export const IUserDataProfilesService = createDecorator('IUserDataProfilesService'); export interface IUserDataProfilesService { readonly _serviceBrand: undefined; diff --git a/src/vs/workbench/browser/actions/developerActions.ts b/src/vs/workbench/browser/actions/developerActions.ts index b6967769f05..5a0d08a8623 100644 --- a/src/vs/workbench/browser/actions/developerActions.ts +++ b/src/vs/workbench/browser/actions/developerActions.ts @@ -273,7 +273,7 @@ class LogStorageAction extends Action2 { } run(accessor: ServicesAccessor): void { - accessor.get(IStorageService).logStorage(); + accessor.get(IStorageService).log(); } } diff --git a/src/vs/workbench/contrib/userDataProfile/common/profileActions.ts b/src/vs/workbench/contrib/userDataProfile/common/profileActions.ts index bf473a183e5..698833c1aa8 100644 --- a/src/vs/workbench/contrib/userDataProfile/common/profileActions.ts +++ b/src/vs/workbench/contrib/userDataProfile/common/profileActions.ts @@ -14,7 +14,7 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { INotificationService } from 'vs/platform/notification/common/notification'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { asJson, asText, IRequestService } from 'vs/platform/request/common/request'; -import { IUserDataProfileTemplate, isProfile, IUserDataProfileWorkbenchService, PROFILES_CATEGORY, PROFILE_EXTENSION, PROFILE_FILTER } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; +import { IUserDataProfileTemplate, isUserDataProfileTemplate, IUserDataProfileWorkbenchService, PROFILES_CATEGORY, PROFILE_EXTENSION, PROFILE_FILTER } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; registerAction2(class ExportProfileAction extends Action2 { @@ -118,7 +118,7 @@ registerAction2(class ImportProfileAction extends Action2 { } const content = (await fileService.readFile(profileLocation[0])).value.toString(); const parsed = JSON.parse(content); - return isProfile(parsed) ? parsed : null; + return isUserDataProfileTemplate(parsed) ? parsed : null; } private async getProfileFromURL(url: string, requestService: IRequestService): Promise { @@ -126,7 +126,7 @@ registerAction2(class ImportProfileAction extends Action2 { const context = await requestService.request(options, CancellationToken.None); if (context.res.statusCode === 200) { const result = await asJson(context); - return isProfile(result) ? result : null; + return isUserDataProfileTemplate(result) ? result : null; } else { const message = await asText(context); throw new Error(`Expected 200, got back ${context.res.statusCode} instead.\n\n${message}`); diff --git a/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts b/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts index e4db0c6926b..1d72c8ac7fa 100644 --- a/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts +++ b/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts @@ -14,7 +14,7 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { INotificationService } from 'vs/platform/notification/common/notification'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { asJson, asText, IRequestService } from 'vs/platform/request/common/request'; -import { IUserDataProfileTemplate, isProfile, IUserDataProfileManagementService, IUserDataProfileWorkbenchService, PROFILES_CATEGORY, PROFILE_EXTENSION, PROFILE_FILTER, ManageProfilesSubMenu } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; +import { IUserDataProfileTemplate, isUserDataProfileTemplate, IUserDataProfileManagementService, IUserDataProfileWorkbenchService, PROFILES_CATEGORY, PROFILE_EXTENSION, PROFILE_FILTER, ManageProfilesSubMenu } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IsDevelopmentContext } from 'vs/platform/contextkey/common/contextkeys'; @@ -293,7 +293,7 @@ registerAction2(class ImportProfileAction extends Action2 { } const content = (await fileService.readFile(profileLocation[0])).value.toString(); const parsed = JSON.parse(content); - return isProfile(parsed) ? parsed : null; + return isUserDataProfileTemplate(parsed) ? parsed : null; } private async getProfileFromURL(url: string, requestService: IRequestService): Promise { @@ -301,7 +301,7 @@ registerAction2(class ImportProfileAction extends Action2 { const context = await requestService.request(options, CancellationToken.None); if (context.res.statusCode === 200) { const result = await asJson(context); - return isProfile(result) ? result : null; + return isUserDataProfileTemplate(result) ? result : null; } else { const message = await asText(context); throw new Error(`Expected 200, got back ${context.res.statusCode} instead.\n\n${message}`); diff --git a/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts b/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts index 154581f41a9..7a49cb852de 100644 --- a/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts +++ b/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts @@ -36,7 +36,7 @@ export interface IUserDataProfileTemplate { readonly extensions?: string; } -export function isProfile(thing: any): thing is IUserDataProfileTemplate { +export function isUserDataProfileTemplate(thing: unknown): thing is IUserDataProfileTemplate { const candidate = thing as IUserDataProfileTemplate | undefined; return !!(candidate && typeof candidate === 'object' diff --git a/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts b/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts index e63f50d9d35..0cbfcf1e2b8 100644 --- a/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts @@ -164,7 +164,7 @@ export class NativeWorkspaceEditingService extends AbstractWorkspaceEditingServi if (result) { // Migrate storage to new workspace - await this.storageService.migrate(result.workspace); + await this.storageService.switch(result.workspace, true /* preserve data */); // Reinitialize backup service if (this.workingCopyBackupService instanceof WorkingCopyBackupService) { From 3d4a1bc11ecddeafb786d47452f40adafa737d59 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 17 Jun 2022 09:04:35 +0200 Subject: [PATCH 130/175] Split IUserDataProfilesService (#152385) * - extract IUserDataProfileService from IUserDataProfilesService - Adopt it * :lipstick: Co-authored-by: Benjamin Pasero --- .../sharedProcess/sharedProcessMain.ts | 10 +- src/vs/code/electron-main/app.ts | 5 +- src/vs/code/electron-main/main.ts | 5 +- src/vs/code/node/cliProcessMain.ts | 2 +- .../common/extensionManagementIpc.ts | 18 +-- .../storage/browser/storageService.ts | 4 +- .../electron-sandbox/storageService.ts | 8 +- .../test/browser/storageService.test.ts | 11 +- .../electron-main/storageMainService.test.ts | 2 +- .../test/browser/fileUserDataProvider.test.ts | 52 +++--- .../userDataProfile/common/userDataProfile.ts | 51 +++--- .../common/userDataProfileService.ts | 35 ++++ .../electron-main/userDataProfile.ts | 19 ++- .../electron-sandbox/userDataProfile.ts | 26 ++- .../test/common/userDataSyncClient.ts | 2 +- .../node/remoteExtensionHostAgentCli.ts | 2 +- src/vs/server/node/serverServices.ts | 2 +- src/vs/workbench/browser/web.main.ts | 19 ++- .../browser/keybindingsEditorContribution.ts | 12 +- .../browser/preferences.contribution.ts | 4 +- .../common/preferencesContribution.ts | 6 +- .../snippets/browser/configureSnippets.ts | 12 +- .../snippets/browser/snippetsService.ts | 6 +- .../browser/telemetry.contribution.ts | 10 +- .../browser/userDataProfile.ts | 18 +-- .../common/userDataProfileActions.ts | 16 +- .../electron-sandbox/desktop.main.ts | 20 +-- .../browser/configurationService.ts | 6 +- .../common/configurationEditingService.ts | 8 +- .../configurationEditingService.test.ts | 30 ++-- .../test/browser/configurationService.test.ts | 152 +++++++++--------- .../extensionManagementServerService.ts | 2 +- .../extensionManagementServerService.ts | 14 +- .../remoteExtensionManagementService.ts | 2 +- .../cachedExtensionScanner.ts | 6 +- .../browser/extensionStorageMigration.test.ts | 6 +- .../keybinding/browser/keybindingService.ts | 6 +- .../keybinding/common/keybindingEditing.ts | 6 +- .../test/browser/keybindingEditing.test.ts | 20 +-- .../preferences/browser/preferencesService.ts | 8 +- .../browser/userDataProfileManagement.ts | 17 +- .../userDataProfile/common/settingsProfile.ts | 12 +- .../test/browser/workbenchTestServices.ts | 6 +- .../electron-browser/workbenchTestServices.ts | 6 +- 44 files changed, 375 insertions(+), 309 deletions(-) create mode 100644 src/vs/platform/userDataProfile/common/userDataProfileService.ts diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 918ec2e5e40..5972cfa8985 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -100,11 +100,12 @@ import { InspectProfilingService as V8InspectProfilingService } from 'vs/platfor import { IV8InspectProfilingService } from 'vs/platform/profiling/common/profiling'; import { IExtensionsScannerService } from 'vs/platform/extensionManagement/common/extensionsScannerService'; import { ExtensionsScannerService } from 'vs/platform/extensionManagement/node/extensionsScannerService'; -import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService, IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { ExtensionsProfileScannerService, IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; import { PolicyChannelClient } from 'vs/platform/policy/common/policyIpc'; import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; import { OneDataSystemAppender } from 'vs/platform/telemetry/node/1dsAppender'; +import { UserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfileService'; // import { OneDataSystemAppender } from 'vs/platform/telemetry/node/1dsAppender'; class SharedProcessMain extends Disposable { @@ -232,15 +233,18 @@ class SharedProcessMain extends Disposable { fileService.registerProvider(Schemas.vscodeUserData, userDataFileSystemProvider); // User Data Profiles - const userDataProfilesService = this._register(new UserDataProfilesService(this.configuration.defaultProfile, undefined, environmentService, fileService, logService)); + const userDataProfilesService = this._register(new UserDataProfilesService(this.configuration.defaultProfile, environmentService, fileService, logService)); services.set(IUserDataProfilesService, userDataProfilesService); + const userDataProfileService = this._register(new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService.defaultProfile)); + services.set(IUserDataProfileService, userDataProfileService); + // Configuration const configurationService = this._register(new ConfigurationService(userDataProfilesService.defaultProfile.settingsResource, fileService, policyService, logService)); services.set(IConfigurationService, configurationService); // Storage (global access only) - const storageService = new NativeStorageService(undefined, mainProcessService, userDataProfilesService, environmentService); + const storageService = new NativeStorageService(undefined, mainProcessService, userDataProfileService, environmentService); services.set(IStorageService, storageService); this._register(toDisposable(() => storageService.flush())); diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 6db5653c631..4f0f279867b 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -99,9 +99,9 @@ import { IWorkspacesHistoryMainService, WorkspacesHistoryMainService } from 'vs/ import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; import { IWorkspacesManagementMainService, WorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService'; import { CredentialsNativeMainService } from 'vs/platform/credentials/electron-main/credentialsMainService'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IPolicyService } from 'vs/platform/policy/common/policy'; import { PolicyChannel } from 'vs/platform/policy/common/policyIpc'; +import { IUserDataProfilesMainService } from 'vs/platform/userDataProfile/electron-main/userDataProfile'; /** * The main VS Code application. There will only ever be one instance, @@ -711,8 +711,7 @@ export class CodeApplication extends Disposable { sharedProcessClient.then(client => client.registerChannel(LOCAL_FILE_SYSTEM_CHANNEL_NAME, fileSystemProviderChannel)); // Profiles - const userDataProfilesService = ProxyChannel.fromService(accessor.get(IUserDataProfilesService)); - mainProcessElectronServer.registerChannel('userDataProfiles', userDataProfilesService); + mainProcessElectronServer.registerChannel('userDataProfiles', ProxyChannel.fromService(accessor.get(IUserDataProfilesMainService))); // Update const updateChannel = new UpdateChannel(accessor.get(IUpdateService)); diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index d846dcb1304..333b118eee3 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -62,8 +62,7 @@ import { IStateMainService } from 'vs/platform/state/electron-main/state'; import { StateMainService } from 'vs/platform/state/electron-main/stateMainService'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { IThemeMainService, ThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; -import { UserDataProfilesMainService } from 'vs/platform/userDataProfile/electron-main/userDataProfile'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfilesMainService, UserDataProfilesMainService } from 'vs/platform/userDataProfile/electron-main/userDataProfile'; import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; import { NativePolicyService } from 'vs/platform/policy/node/nativePolicyService'; import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; @@ -185,7 +184,7 @@ class CodeMain { // User Data Profiles const userDataProfilesMainService = new UserDataProfilesMainService(stateMainService, uriIdentityService, environmentMainService, fileService, logService); - services.set(IUserDataProfilesService, userDataProfilesMainService); + services.set(IUserDataProfilesMainService, userDataProfilesMainService); // Policy const policyService = isWindows && productService.win32RegValueName ? disposables.add(new NativePolicyService(productService.win32RegValueName)) diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 19a7e059ea9..3cda56cf0e5 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -134,7 +134,7 @@ class CliMain extends Disposable { fileService.registerProvider(Schemas.file, diskFileSystemProvider); // User Data Profiles - const userDataProfilesService = new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService); + const userDataProfilesService = new UserDataProfilesService(undefined, environmentService, fileService, logService); services.set(IUserDataProfilesService, userDataProfilesService); // Policy diff --git a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts index c55491edf17..2fa48c8f532 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts @@ -12,7 +12,6 @@ import { DefaultURITransformer, IURITransformer, transformAndReviveIncomingURIs import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { DidUninstallExtensionEvent, IExtensionIdentifier, IExtensionManagementService, IExtensionTipsService, IGalleryExtension, IGalleryMetadata, ILocalExtension, InstallExtensionEvent, InstallExtensionResult, InstallOptions, InstallVSIXOptions, IExtensionsControlManifest, isTargetPlatformCompatible, UninstallOptions, IServerExtensionManagementService, ServerInstallOptions, ServerInstallVSIXOptions, ServerUninstallOptions, Metadata } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionType, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; function transformIncomingURI(uri: UriComponents, transformer: IURITransformer | null): URI { return URI.revive(transformer ? transformer.transformIncoming(uri) : uri); @@ -98,10 +97,7 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt private readonly _onDidUninstallExtension = this._register(new Emitter()); readonly onDidUninstallExtension = this._onDidUninstallExtension.event; - constructor( - private readonly channel: IChannel, - private readonly userDataProfilesService: IUserDataProfilesService | undefined - ) { + constructor(private readonly channel: IChannel) { super(); this._register(this.channel.listen('onInstallExtension')(e => this._onInstallExtension.fire({ identifier: e.identifier, source: this.isUriComponents(e.source) ? URI.revive(e.source) : e.source }))); this._register(this.channel.listen('onDidInstallExtensions')(results => this._onDidInstallExtensions.fire(results.map(e => ({ ...e, local: e.local ? transformIncomingExtension(e.local, null) : e.local, source: this.isUriComponents(e.source) ? URI.revive(e.source) : e.source }))))); @@ -139,7 +135,7 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt } install(vsix: URI, options?: InstallVSIXOptions): Promise { - const serverInstallOptions: ServerInstallVSIXOptions = { ...options, profileLocation: this.userDataProfilesService?.currentProfile.extensionsResource }; + const serverInstallOptions: ServerInstallVSIXOptions = { ...options, profileLocation: this.getExtensionsProfileResource() }; return Promise.resolve(this.channel.call('install', [vsix, serverInstallOptions])).then(local => transformIncomingExtension(local, null)); } @@ -148,12 +144,12 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt } installFromGallery(extension: IGalleryExtension, installOptions?: InstallOptions): Promise { - const serverInstallOptions: ServerInstallOptions = { ...installOptions, profileLocation: this.userDataProfilesService?.currentProfile.extensionsResource }; + const serverInstallOptions: ServerInstallOptions = { ...installOptions, profileLocation: this.getExtensionsProfileResource() }; return Promise.resolve(this.channel.call('installFromGallery', [extension, serverInstallOptions])).then(local => transformIncomingExtension(local, null)); } uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise { - const serverUninstallOptions: ServerUninstallOptions = { ...options, profileLocation: this.userDataProfilesService?.currentProfile.extensionsResource }; + const serverUninstallOptions: ServerUninstallOptions = { ...options, profileLocation: this.getExtensionsProfileResource() }; return Promise.resolve(this.channel.call('uninstall', [extension!, serverUninstallOptions])); } @@ -162,7 +158,7 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt } getInstalled(type: ExtensionType | null = null): Promise { - return Promise.resolve(this.channel.call('getInstalled', [type, this.userDataProfilesService?.currentProfile.extensionsResource])) + return Promise.resolve(this.channel.call('getInstalled', [type, this.getExtensionsProfileResource()])) .then(extensions => extensions.map(extension => transformIncomingExtension(extension, null))); } @@ -185,6 +181,10 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt } registerParticipant() { throw new Error('Not Supported'); } + + protected getExtensionsProfileResource(): URI | undefined { + return undefined; + } } export class ExtensionTipsChannel implements IServerChannel { diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts index 219aed37b5a..6a22eae6de8 100644 --- a/src/vs/platform/storage/browser/storageService.ts +++ b/src/vs/platform/storage/browser/storageService.ts @@ -13,7 +13,7 @@ import { assertIsDefined } from 'vs/base/common/types'; import { InMemoryStorageDatabase, isStorageItemsChangeEvent, IStorage, IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest, Storage } from 'vs/base/parts/storage/common/storage'; import { ILogService } from 'vs/platform/log/common/log'; import { AbstractStorageService, IS_NEW_KEY, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile, IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IAnyWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export class BrowserStorageService extends AbstractStorageService { @@ -43,7 +43,7 @@ export class BrowserStorageService extends AbstractStorageService { constructor( private readonly payload: IAnyWorkspaceIdentifier, @ILogService private readonly logService: ILogService, - @IUserDataProfilesService userDataProfileService: IUserDataProfilesService + @IUserDataProfileService userDataProfileService: IUserDataProfileService ) { super({ flushInterval: BrowserStorageService.BROWSER_DEFAULT_FLUSH_INTERVAL }); diff --git a/src/vs/platform/storage/electron-sandbox/storageService.ts b/src/vs/platform/storage/electron-sandbox/storageService.ts index cd8274acd5c..dcf551da3ac 100644 --- a/src/vs/platform/storage/electron-sandbox/storageService.ts +++ b/src/vs/platform/storage/electron-sandbox/storageService.ts @@ -11,7 +11,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services'; import { AbstractStorageService, StorageScope, WillSaveStateReason } from 'vs/platform/storage/common/storage'; import { ApplicationStorageDatabaseClient, GlobalStorageDatabaseClient, WorkspaceStorageDatabaseClient } from 'vs/platform/storage/common/storageIpc'; -import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile, IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IAnyWorkspaceIdentifier, IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export class NativeStorageService extends AbstractStorageService { @@ -30,15 +30,15 @@ export class NativeStorageService extends AbstractStorageService { constructor( workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined, private readonly mainProcessService: IMainProcessService, - userDataProfilesService: IUserDataProfilesService, + userDataProfileService: IUserDataProfileService, private readonly environmentService: IEnvironmentService ) { super(); - this.applicationStorageProfile = userDataProfilesService.defaultProfile; + this.applicationStorageProfile = userDataProfileService.defaultProfile; this.applicationStorage = this.createApplicationStorage(); - this.globalStorage = this.createGlobalStorage(userDataProfilesService.currentProfile); + this.globalStorage = this.createGlobalStorage(userDataProfileService.currentProfile); this.workspaceStorage = this.createWorkspaceStorage(workspace); } diff --git a/src/vs/platform/storage/test/browser/storageService.test.ts b/src/vs/platform/storage/test/browser/storageService.test.ts index 91bd5f6ebed..0d6104ca19d 100644 --- a/src/vs/platform/storage/test/browser/storageService.test.ts +++ b/src/vs/platform/storage/test/browser/storageService.test.ts @@ -9,17 +9,16 @@ import { Schemas } from 'vs/base/common/network'; import { joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { Storage } from 'vs/base/parts/storage/common/storage'; -import { mock } from 'vs/base/test/common/mock'; import { flakySuite } from 'vs/base/test/common/testUtils'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { FileService } from 'vs/platform/files/common/fileService'; import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; import { NullLogService } from 'vs/platform/log/common/log'; import { BrowserStorageService, IndexedDBStorageDatabase } from 'vs/platform/storage/browser/storageService'; import { StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { createSuite } from 'vs/platform/storage/test/common/storageService.test'; -import { IUserDataProfile, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { UserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfileService'; async function createStorageService(): Promise<[DisposableStore, BrowserStorageService]> { const disposables = new DisposableStore(); @@ -32,10 +31,6 @@ async function createStorageService(): Promise<[DisposableStore, BrowserStorageS const profilesRoot = URI.file('/profiles').with({ scheme: Schemas.inMemory }); - class EnvironmentServiceMock extends mock() { - override readonly userRoamingDataHome = profilesRoot; - } - const inMemoryDefaultProfileRoot = joinPath(profilesRoot, 'default'); const inMemoryDefaultProfile: IUserDataProfile = { id: 'id', @@ -64,7 +59,7 @@ async function createStorageService(): Promise<[DisposableStore, BrowserStorageS extensionsResource: joinPath(inMemoryExtraProfileRoot, 'extensionsResource') }; - const userDataProfileService = new UserDataProfilesService(inMemoryDefaultProfile, inMemoryExtraProfile, new EnvironmentServiceMock(), fileService, new NullLogService()); + const userDataProfileService = new UserDataProfileService(inMemoryDefaultProfile, inMemoryExtraProfile); const storageService = disposables.add(new BrowserStorageService({ id: 'workspace-storage-test' }, logService, userDataProfileService)); diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index 63292faaff4..4150fa1fc54 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -145,7 +145,7 @@ suite('StorageMainService', function () { function createStorageService(lifecycleMainService: ILifecycleMainService = new StorageTestLifecycleMainService()): TestStorageMainService { const environmentService = new NativeEnvironmentService(parseArgs(process.argv, OPTIONS), productService); const fileService = new FileService(new NullLogService()); - return new TestStorageMainService(new NullLogService(), environmentService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, new NullLogService()), lifecycleMainService, fileService); + return new TestStorageMainService(new NullLogService(), environmentService, new UserDataProfilesService(undefined, environmentService, fileService, new NullLogService()), lifecycleMainService, fileService); } test('basics (application)', function () { diff --git a/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts b/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts index 7df2c12094a..00f18d5163b 100644 --- a/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts +++ b/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts @@ -52,7 +52,7 @@ suite('FileUserDataProvider', () => { await testObject.createFolder(backupWorkspaceHomeOnDisk); environmentService = new TestEnvironmentService(userDataHomeOnDisk); - userDataProfilesService = new UserDataProfilesService(undefined, undefined, environmentService, testObject, logService); + userDataProfilesService = new UserDataProfilesService(undefined, environmentService, testObject, logService); fileUserDataProvider = new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, logService); disposables.add(fileUserDataProvider); @@ -62,25 +62,25 @@ suite('FileUserDataProvider', () => { teardown(() => disposables.clear()); test('exists return false when file does not exist', async () => { - const exists = await testObject.exists(userDataProfilesService.currentProfile.settingsResource); + const exists = await testObject.exists(userDataProfilesService.defaultProfile.settingsResource); assert.strictEqual(exists, false); }); test('read file throws error if not exist', async () => { try { - await testObject.readFile(userDataProfilesService.currentProfile.settingsResource); + await testObject.readFile(userDataProfilesService.defaultProfile.settingsResource); assert.fail('Should fail since file does not exist'); } catch (e) { } }); test('read existing file', async () => { await testObject.writeFile(joinPath(userDataHomeOnDisk, 'settings.json'), VSBuffer.fromString('{}')); - const result = await testObject.readFile(userDataProfilesService.currentProfile.settingsResource); + const result = await testObject.readFile(userDataProfilesService.defaultProfile.settingsResource); assert.strictEqual(result.value.toString(), '{}'); }); test('create file', async () => { - const resource = userDataProfilesService.currentProfile.settingsResource; + const resource = userDataProfilesService.defaultProfile.settingsResource; const actual1 = await testObject.createFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual2 = await testObject.readFile(joinPath(userDataHomeOnDisk, 'settings.json')); @@ -88,7 +88,7 @@ suite('FileUserDataProvider', () => { }); test('write file creates the file if not exist', async () => { - const resource = userDataProfilesService.currentProfile.settingsResource; + const resource = userDataProfilesService.defaultProfile.settingsResource; const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual2 = await testObject.readFile(joinPath(userDataHomeOnDisk, 'settings.json')); @@ -96,7 +96,7 @@ suite('FileUserDataProvider', () => { }); test('write to existing file', async () => { - const resource = userDataProfilesService.currentProfile.settingsResource; + const resource = userDataProfilesService.defaultProfile.settingsResource; await testObject.writeFile(joinPath(userDataHomeOnDisk, 'settings.json'), VSBuffer.fromString('{}')); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{a:1}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); @@ -106,33 +106,33 @@ suite('FileUserDataProvider', () => { test('delete file', async () => { await testObject.writeFile(joinPath(userDataHomeOnDisk, 'settings.json'), VSBuffer.fromString('')); - await testObject.del(userDataProfilesService.currentProfile.settingsResource); + await testObject.del(userDataProfilesService.defaultProfile.settingsResource); const result = await testObject.exists(joinPath(userDataHomeOnDisk, 'settings.json')); assert.strictEqual(false, result); }); test('resolve file', async () => { await testObject.writeFile(joinPath(userDataHomeOnDisk, 'settings.json'), VSBuffer.fromString('')); - const result = await testObject.resolve(userDataProfilesService.currentProfile.settingsResource); + const result = await testObject.resolve(userDataProfilesService.defaultProfile.settingsResource); assert.ok(!result.isDirectory); assert.ok(result.children === undefined); }); test('exists return false for folder that does not exist', async () => { - const exists = await testObject.exists(userDataProfilesService.currentProfile.snippetsHome); + const exists = await testObject.exists(userDataProfilesService.defaultProfile.snippetsHome); assert.strictEqual(exists, false); }); test('exists return true for folder that exists', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); - const exists = await testObject.exists(userDataProfilesService.currentProfile.snippetsHome); + const exists = await testObject.exists(userDataProfilesService.defaultProfile.snippetsHome); assert.strictEqual(exists, true); }); test('read file throws error for folder', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); try { - await testObject.readFile(userDataProfilesService.currentProfile.snippetsHome); + await testObject.readFile(userDataProfilesService.defaultProfile.snippetsHome); assert.fail('Should fail since read file is not supported for folders'); } catch (e) { } }); @@ -140,7 +140,7 @@ suite('FileUserDataProvider', () => { test('read file under folder', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json'), VSBuffer.fromString('{}')); - const resource = joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json'); + const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json'); const actual = await testObject.readFile(resource); assert.strictEqual(actual.resource.toString(), resource.toString()); assert.strictEqual(actual.value.toString(), '{}'); @@ -149,7 +149,7 @@ suite('FileUserDataProvider', () => { test('read file under sub folder', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets', 'java')); await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'java', 'settings.json'), VSBuffer.fromString('{}')); - const resource = joinPath(userDataProfilesService.currentProfile.snippetsHome, 'java/settings.json'); + const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'java/settings.json'); const actual = await testObject.readFile(resource); assert.strictEqual(actual.resource.toString(), resource.toString()); assert.strictEqual(actual.value.toString(), '{}'); @@ -157,7 +157,7 @@ suite('FileUserDataProvider', () => { test('create file under folder that exists', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); - const resource = joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json'); + const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json'); const actual1 = await testObject.createFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual2 = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); @@ -165,7 +165,7 @@ suite('FileUserDataProvider', () => { }); test('create file under folder that does not exist', async () => { - const resource = joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json'); + const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json'); const actual1 = await testObject.createFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual2 = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); @@ -174,7 +174,7 @@ suite('FileUserDataProvider', () => { test('write to not existing file under container that exists', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); - const resource = joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json'); + const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json'); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); @@ -182,7 +182,7 @@ suite('FileUserDataProvider', () => { }); test('write to not existing file under container that does not exists', async () => { - const resource = joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json'); + const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json'); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); @@ -192,7 +192,7 @@ suite('FileUserDataProvider', () => { test('write to existing file under container', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json'), VSBuffer.fromString('{}')); - const resource = joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json'); + const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json'); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{a:1}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); @@ -200,7 +200,7 @@ suite('FileUserDataProvider', () => { }); test('write file under sub container', async () => { - const resource = joinPath(userDataProfilesService.currentProfile.snippetsHome, 'java/settings.json'); + const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'java/settings.json'); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'java', 'settings.json')); @@ -209,7 +209,7 @@ suite('FileUserDataProvider', () => { test('delete throws error for folder that does not exist', async () => { try { - await testObject.del(userDataProfilesService.currentProfile.snippetsHome); + await testObject.del(userDataProfilesService.defaultProfile.snippetsHome); assert.fail('Should fail the folder does not exist'); } catch (e) { } }); @@ -217,14 +217,14 @@ suite('FileUserDataProvider', () => { test('delete not existing file under container that exists', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); try { - await testObject.del(joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json')); + await testObject.del(joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json')); assert.fail('Should fail since file does not exist'); } catch (e) { } }); test('delete not existing file under container that does not exists', async () => { try { - await testObject.del(joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json')); + await testObject.del(joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json')); assert.fail('Should fail since file does not exist'); } catch (e) { } }); @@ -232,7 +232,7 @@ suite('FileUserDataProvider', () => { test('delete existing file under folder', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json'), VSBuffer.fromString('{}')); - await testObject.del(joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json')); + await testObject.del(joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json')); const exists = await testObject.exists(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); assert.strictEqual(exists, false); }); @@ -240,11 +240,11 @@ suite('FileUserDataProvider', () => { test('resolve folder', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json'), VSBuffer.fromString('{}')); - const result = await testObject.resolve(userDataProfilesService.currentProfile.snippetsHome); + const result = await testObject.resolve(userDataProfilesService.defaultProfile.snippetsHome); assert.ok(result.isDirectory); assert.ok(result.children !== undefined); assert.strictEqual(result.children!.length, 1); - assert.strictEqual(result.children![0].resource.toString(), joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json').toString()); + assert.strictEqual(result.children![0].resource.toString(), joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json').toString()); }); test('read backup file', async () => { diff --git a/src/vs/platform/userDataProfile/common/userDataProfile.ts b/src/vs/platform/userDataProfile/common/userDataProfile.ts index d6629f8c0c5..73ec2a89ff7 100644 --- a/src/vs/platform/userDataProfile/common/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/common/userDataProfile.ts @@ -47,6 +47,15 @@ export interface IUserDataProfile { readonly extensionsResource: URI | undefined; } +export const IUserDataProfileService = createDecorator('IUserDataProfileService'); +export interface IUserDataProfileService { + readonly _serviceBrand: undefined; + readonly defaultProfile: IUserDataProfile; + readonly onDidChangeCurrentProfile: Event; + readonly currentProfile: IUserDataProfile; + updateCurrentProfile(currentProfile: IUserDataProfile): void; +} + export function isUserDataProfile(thing: unknown): thing is IUserDataProfile { const candidate = thing as IUserDataProfile | undefined; @@ -70,7 +79,6 @@ export interface IUserDataProfilesService { readonly profilesHome: URI; readonly defaultProfile: IUserDataProfile; - readonly currentProfile: IUserDataProfile; readonly onDidChangeProfiles: Event; readonly profiles: IUserDataProfile[]; @@ -79,7 +87,6 @@ export interface IUserDataProfilesService { createProfile(profile: IUserDataProfile, options: ProfileOptions, workspaceIdentifier?: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise; setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise; getProfile(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): IUserDataProfile; - getAllProfiles(): Promise; removeProfile(profile: IUserDataProfile): Promise; } @@ -98,14 +105,26 @@ export function reviveProfile(profile: UriDto, scheme: string) }; } +export function toUserDataProfile(name: string, location: URI, options: ProfileOptions, defaultProfile: true | IUserDataProfile): IUserDataProfile { + return { + id: hash(location.toString()).toString(16), + name: name, + location: location, + isDefault: defaultProfile === true, + globalStorageHome: defaultProfile === true || options.uiState ? joinPath(location, 'globalStorage') : defaultProfile.globalStorageHome, + settingsResource: defaultProfile === true || options.settings ? joinPath(location, 'settings.json') : defaultProfile.settingsResource, + keybindingsResource: defaultProfile === true || options.keybindings ? joinPath(location, 'keybindings.json') : defaultProfile.keybindingsResource, + tasksResource: defaultProfile === true || options.tasks ? joinPath(location, 'tasks.json') : defaultProfile.tasksResource, + snippetsHome: defaultProfile === true || options.snippets ? joinPath(location, 'snippets') : defaultProfile.snippetsHome, + extensionsResource: defaultProfile === true && !options.extensions ? undefined : joinPath(location, 'extensions.json'), + }; +} + export class UserDataProfilesService extends Disposable implements IUserDataProfilesService { readonly _serviceBrand: undefined; readonly profilesHome: URI; - protected _currentProfile: IUserDataProfile; - get currentProfile(): IUserDataProfile { return this._currentProfile; } - protected _defaultProfile: IUserDataProfile; get defaultProfile(): IUserDataProfile { return this._defaultProfile; } @@ -116,37 +135,19 @@ export class UserDataProfilesService extends Disposable implements IUserDataProf constructor( defaultProfile: UriDto | undefined, - currentProfile: UriDto | undefined, @IEnvironmentService protected readonly environmentService: IEnvironmentService, @IFileService protected readonly fileService: IFileService, @ILogService protected readonly logService: ILogService ) { super(); this.profilesHome = joinPath(this.environmentService.userRoamingDataHome, 'profiles'); - this._defaultProfile = defaultProfile ? reviveProfile(defaultProfile, this.profilesHome.scheme) : this.toUserDataProfile(localize('defaultProfile', "Default"), environmentService.userRoamingDataHome, { ...DefaultOptions, extensions: false }, true); - this._currentProfile = currentProfile ? reviveProfile(currentProfile, this.profilesHome.scheme) : this._defaultProfile; + this._defaultProfile = defaultProfile ? reviveProfile(defaultProfile, this.profilesHome.scheme) : toUserDataProfile(localize('defaultProfile', "Default"), environmentService.userRoamingDataHome, { ...DefaultOptions, extensions: false }, true); } newProfile(name: string, options: ProfileOptions = DefaultOptions): IUserDataProfile { - return this.toUserDataProfile(name, joinPath(this.profilesHome, hash(name).toString(16)), options, this.defaultProfile); + return toUserDataProfile(name, joinPath(this.profilesHome, hash(name).toString(16)), options, this.defaultProfile); } - protected toUserDataProfile(name: string, location: URI, options: ProfileOptions, defaultProfile: true | IUserDataProfile): IUserDataProfile { - return { - id: hash(location.toString()).toString(16), - name: name, - location: location, - isDefault: defaultProfile === true, - globalStorageHome: defaultProfile === true || options.uiState ? joinPath(location, 'globalStorage') : defaultProfile.globalStorageHome, - settingsResource: defaultProfile === true || options.settings ? joinPath(location, 'settings.json') : defaultProfile.settingsResource, - keybindingsResource: defaultProfile === true || options.keybindings ? joinPath(location, 'keybindings.json') : defaultProfile.keybindingsResource, - tasksResource: defaultProfile === true || options.tasks ? joinPath(location, 'tasks.json') : defaultProfile.tasksResource, - snippetsHome: defaultProfile === true || options.snippets ? joinPath(location, 'snippets') : defaultProfile.snippetsHome, - extensionsResource: defaultProfile === true && !options.extensions ? undefined : joinPath(location, 'extensions.json'), - }; - } - - getAllProfiles(): Promise { throw new Error('Not implemented'); } createProfile(profile: IUserDataProfile, options: ProfileOptions, workspaceIdentifier?: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise { throw new Error('Not implemented'); } setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise { throw new Error('Not implemented'); } getProfile(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): IUserDataProfile { throw new Error('Not implemented'); } diff --git a/src/vs/platform/userDataProfile/common/userDataProfileService.ts b/src/vs/platform/userDataProfile/common/userDataProfileService.ts new file mode 100644 index 00000000000..615e34944ef --- /dev/null +++ b/src/vs/platform/userDataProfile/common/userDataProfileService.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 { Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IUserDataProfile, IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; + +export class UserDataProfileService extends Disposable implements IUserDataProfileService { + + readonly _serviceBrand: undefined; + + readonly defaultProfile: IUserDataProfile; + + private readonly _onDidChangeCurrentProfile = this._register(new Emitter()); + readonly onDidChangeCurrentProfile = this._onDidChangeCurrentProfile.event; + + private _currentProfile: IUserDataProfile; + get currentProfile(): IUserDataProfile { return this._currentProfile; } + + constructor( + defaultProfile: IUserDataProfile, + currentProfile: IUserDataProfile, + ) { + super(); + this.defaultProfile = defaultProfile; + this._currentProfile = currentProfile; + } + + updateCurrentProfile(userDataProfile: IUserDataProfile): void { + this._currentProfile = userDataProfile; + this._onDidChangeCurrentProfile.fire(userDataProfile); + } +} diff --git a/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts b/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts index 7b1095ebd52..53b08b880cd 100644 --- a/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts @@ -7,14 +7,21 @@ import { ResourceMap } from 'vs/base/common/map'; import { revive } from 'vs/base/common/marshalling'; import { UriDto } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; +import { localize } from 'vs/nls'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; +import { refineServiceDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { IStateMainService } from 'vs/platform/state/electron-main/state'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; -import { ProfileOptions, DefaultOptions, IUserDataProfile, IUserDataProfilesService, UserDataProfilesService, reviveProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { ProfileOptions, DefaultOptions, IUserDataProfile, IUserDataProfilesService, UserDataProfilesService, reviveProfile, toUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; +export const IUserDataProfilesMainService = refineServiceDecorator(IUserDataProfilesService); +export interface IUserDataProfilesMainService extends IUserDataProfilesService { + getAllProfiles(): Promise; +} + type UserDataProfilesObject = { profiles: IUserDataProfile[]; workspaces: ResourceMap; @@ -31,7 +38,7 @@ type StoredWorkspaceInfo = { profile: URI; }; -export class UserDataProfilesMainService extends UserDataProfilesService implements IUserDataProfilesService { +export class UserDataProfilesMainService extends UserDataProfilesService implements IUserDataProfilesMainService { private static readonly PROFILES_KEY = 'userDataProfiles'; private static readonly WORKSPACE_PROFILE_INFO_KEY = 'workspaceAndProfileInfo'; @@ -43,19 +50,19 @@ export class UserDataProfilesMainService extends UserDataProfilesService impleme @IFileService fileService: IFileService, @ILogService logService: ILogService, ) { - super(undefined, undefined, environmentService, fileService, logService); + super(toUserDataProfile(localize('defaultProfile', "Default"), environmentService.userRoamingDataHome, { ...DefaultOptions, extensions: false }, true), environmentService, fileService, logService); } init(): void { if (this.storedProfiles.length) { - this._defaultProfile = this.toUserDataProfile(this.defaultProfile.name, this.defaultProfile.location, DefaultOptions, true); + this._defaultProfile = toUserDataProfile(this.defaultProfile.name, this.defaultProfile.location, DefaultOptions, true); } } private _profilesObject: UserDataProfilesObject | undefined; private get profilesObject(): UserDataProfilesObject { if (!this._profilesObject) { - const profiles = this.storedProfiles.map(storedProfile => this.toUserDataProfile(storedProfile.name, storedProfile.location, storedProfile.options, this.defaultProfile)); + const profiles = this.storedProfiles.map(storedProfile => toUserDataProfile(storedProfile.name, storedProfile.location, storedProfile.options, this.defaultProfile)); profiles.unshift(this.defaultProfile); const workspaces = this.storedWorskpaceInfos.reduce((workspaces, workspaceProfileInfo) => { const profile = profiles.find(p => this.uriIdentityService.extUri.isEqual(p.location, workspaceProfileInfo.profile)); @@ -71,7 +78,7 @@ export class UserDataProfilesMainService extends UserDataProfilesService impleme override get profiles(): IUserDataProfile[] { return this.profilesObject.profiles; } - override async getAllProfiles(): Promise { + async getAllProfiles(): Promise { return this.profiles; } diff --git a/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts b/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts index b90fc159285..823180e0785 100644 --- a/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts @@ -17,22 +17,23 @@ export class UserDataProfilesNativeService extends UserDataProfilesService imple override get profiles(): IUserDataProfile[] { return this._profiles; } constructor( - defaultProfile: IUserDataProfile, - currentProfile: IUserDataProfile, + defaultProfile: UriDto, private readonly channel: IChannel, @IEnvironmentService environmentService: IEnvironmentService, @IFileService fileService: IFileService, @ILogService logService: ILogService, ) { - super(defaultProfile, currentProfile, environmentService, fileService, logService); - this.getAllProfiles().then(profiles => { - this._profiles = profiles; + super(defaultProfile, environmentService, fileService, logService); + this.initializeProfiles(); + } + + private async initializeProfiles(): Promise { + const result = await this.channel.call[]>('getAllProfiles'); + this._profiles = result.map(profile => reviveProfile(profile, this.profilesHome.scheme)); + this._register(this.channel.listen('onDidChangeProfiles')((profiles) => { + this._profiles = result.map(profile => reviveProfile(profile, this.profilesHome.scheme)); this._onDidChangeProfiles.fire(this._profiles); - this._register(this.channel.listen('onDidChangeProfiles')((profiles) => { - this._profiles = profiles; - this._onDidChangeProfiles.fire(this._profiles); - })); - }); + })); } override async createProfile(profile: IUserDataProfile, options: ProfileOptions, workspaceIdentifier?: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise { @@ -45,11 +46,6 @@ export class UserDataProfilesNativeService extends UserDataProfilesService imple return reviveProfile(result, this.profilesHome.scheme); } - override async getAllProfiles(): Promise { - const result = await this.channel.call[]>('getAllProfiles'); - return result.map(profile => reviveProfile(profile, this.profilesHome.scheme)); - } - override removeProfile(profile: IUserDataProfile): Promise { return this.channel.call('removeProfile', [profile]); } diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts index 968ba2d0e3c..1924fffc3ec 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts @@ -83,7 +83,7 @@ export class UserDataSyncClient extends Disposable { fileService.registerProvider(Schemas.inMemory, new InMemoryFileSystemProvider()); this.instantiationService.stub(IFileService, fileService); - const userDataProfilesService = this.instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService)); + const userDataProfilesService = this.instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, environmentService, fileService, logService)); this.instantiationService.stub(IStorageService, this._register(new InMemoryStorageService())); diff --git a/src/vs/server/node/remoteExtensionHostAgentCli.ts b/src/vs/server/node/remoteExtensionHostAgentCli.ts index f862fc4a7be..520cca772bb 100644 --- a/src/vs/server/node/remoteExtensionHostAgentCli.ts +++ b/src/vs/server/node/remoteExtensionHostAgentCli.ts @@ -94,7 +94,7 @@ class CliMain extends Disposable { fileService.registerProvider(Schemas.file, this._register(new DiskFileSystemProvider(logService))); // User Data Profiles - const userDataProfilesService = this._register(new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService)); + const userDataProfilesService = this._register(new UserDataProfilesService(undefined, environmentService, fileService, logService)); services.set(IUserDataProfilesService, userDataProfilesService); // Configuration diff --git a/src/vs/server/node/serverServices.ts b/src/vs/server/node/serverServices.ts index 4f5a7eb718c..e3e267c1d94 100644 --- a/src/vs/server/node/serverServices.ts +++ b/src/vs/server/node/serverServices.ts @@ -111,7 +111,7 @@ export async function setupServerServices(connectionToken: ServerConnectionToken fileService.registerProvider(Schemas.file, disposables.add(new DiskFileSystemProvider(logService))); // Configuration - const userDataProfilesService = new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService); + const userDataProfilesService = new UserDataProfilesService(undefined, environmentService, fileService, logService); const configurationService = new ConfigurationService(environmentService.machineSettingsResource, fileService, new NullPolicyService(), logService); services.set(IConfigurationService, configurationService); await configurationService.initialize(); diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index ec36613901f..36bd1e287ab 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -73,11 +73,12 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { DelayedLogChannel } from 'vs/workbench/services/output/common/delayedLogChannel'; import { dirname, joinPath } from 'vs/base/common/resources'; -import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService, IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { NullPolicyService } from 'vs/platform/policy/common/policy'; import { IRemoteExplorerService, TunnelSource } from 'vs/workbench/services/remote/common/remoteExplorerService'; import { DisposableTunnel } from 'vs/platform/tunnel/common/tunnel'; import { ILabelService } from 'vs/platform/label/common/label'; +import { UserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfileService'; export class BrowserMain extends Disposable { @@ -258,8 +259,10 @@ export class BrowserMain extends Disposable { await this.registerFileSystemProviders(environmentService, fileService, remoteAgentService, logService, logsPath); // User Data Profiles - const userDataProfilesService = new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService); + const userDataProfilesService = new UserDataProfilesService(undefined, environmentService, fileService, logService); serviceCollection.set(IUserDataProfilesService, userDataProfilesService); + const userDataProfileService = new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService.defaultProfile); + serviceCollection.set(IUserDataProfileService, userDataProfileService); // URI Identity const uriIdentityService = new UriIdentityService(fileService); @@ -267,7 +270,7 @@ export class BrowserMain extends Disposable { // Long running services (workspace, config, storage) const [configurationService, storageService] = await Promise.all([ - this.createWorkspaceService(payload, environmentService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService).then(service => { + this.createWorkspaceService(payload, environmentService, userDataProfileService, fileService, remoteAgentService, uriIdentityService, logService).then(service => { // Workspace serviceCollection.set(IWorkspaceContextService, service); @@ -278,7 +281,7 @@ export class BrowserMain extends Disposable { return service; }), - this.createStorageService(payload, logService, userDataProfilesService).then(service => { + this.createStorageService(payload, logService, userDataProfileService).then(service => { // Storage serviceCollection.set(IStorageService, service); @@ -450,8 +453,8 @@ export class BrowserMain extends Disposable { }); } - private async createStorageService(payload: IAnyWorkspaceIdentifier, logService: ILogService, userDataProfilesService: IUserDataProfilesService): Promise { - const storageService = new BrowserStorageService(payload, logService, userDataProfilesService); + private async createStorageService(payload: IAnyWorkspaceIdentifier, logService: ILogService, userDataProfileService: IUserDataProfileService): Promise { + const storageService = new BrowserStorageService(payload, logService, userDataProfileService); try { await storageService.initialize(); @@ -468,9 +471,9 @@ export class BrowserMain extends Disposable { } } - private async createWorkspaceService(payload: IAnyWorkspaceIdentifier, environmentService: IWorkbenchEnvironmentService, userDataProfilesService: IUserDataProfilesService, fileService: FileService, remoteAgentService: IRemoteAgentService, uriIdentityService: IUriIdentityService, logService: ILogService): Promise { + private async createWorkspaceService(payload: IAnyWorkspaceIdentifier, environmentService: IWorkbenchEnvironmentService, userDataProfileService: IUserDataProfileService, fileService: FileService, remoteAgentService: IRemoteAgentService, uriIdentityService: IUriIdentityService, logService: ILogService): Promise { const configurationCache = new ConfigurationCache([Schemas.file, Schemas.vscodeUserData, Schemas.tmp] /* Cache all non native resources */, environmentService, fileService); - const workspaceService = new WorkspaceService({ remoteAuthority: this.configuration.remoteAuthority, configurationCache }, environmentService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService, new NullPolicyService()); + const workspaceService = new WorkspaceService({ remoteAuthority: this.configuration.remoteAuthority, configurationCache }, environmentService, userDataProfileService, fileService, remoteAgentService, uriIdentityService, logService, new NullPolicyService()); try { await workspaceService.initialize(payload); diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts index 1f36a446b97..8a6ed721d99 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts @@ -32,7 +32,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { equals } from 'vs/base/common/arrays'; import { assertIsDefined } from 'vs/base/common/types'; import { isEqual } from 'vs/base/common/resources'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; const NLS_LAUNCH_MESSAGE = nls.localize('defineKeybinding.start', "Define Keybinding"); const NLS_KB_LAYOUT_ERROR_MESSAGE = nls.localize('defineKeybinding.kbLayoutErrorMessage', "You won't be able to produce this key combination under your current keyboard layout."); @@ -51,7 +51,7 @@ export class DefineKeybindingController extends Disposable implements IEditorCon constructor( private _editor: ICodeEditor, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IUserDataProfilesService private readonly _userDataProfilesService: IUserDataProfilesService + @IUserDataProfileService private readonly _userDataProfileService: IUserDataProfileService ) { super(); @@ -70,7 +70,7 @@ export class DefineKeybindingController extends Disposable implements IEditorCon } private _update(): void { - if (!isInterestingEditorModel(this._editor, this._userDataProfilesService)) { + if (!isInterestingEditorModel(this._editor, this._userDataProfileService)) { this._disposeKeybindingWidgetRenderer(); this._disposeKeybindingDecorationRenderer(); return; @@ -365,7 +365,7 @@ class DefineKeybindingCommand extends EditorCommand { } runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor): void { - if (!isInterestingEditorModel(editor, accessor.get(IUserDataProfilesService)) || editor.getOption(EditorOption.readOnly)) { + if (!isInterestingEditorModel(editor, accessor.get(IUserDataProfileService)) || editor.getOption(EditorOption.readOnly)) { return; } const controller = DefineKeybindingController.get(editor); @@ -375,12 +375,12 @@ class DefineKeybindingCommand extends EditorCommand { } } -function isInterestingEditorModel(editor: ICodeEditor, userDataProfilesService: IUserDataProfilesService): boolean { +function isInterestingEditorModel(editor: ICodeEditor, userDataProfileService: IUserDataProfileService): boolean { const model = editor.getModel(); if (!model) { return false; } - return isEqual(model.uri, userDataProfilesService.currentProfile.keybindingsResource); + return isEqual(model.uri, userDataProfileService.currentProfile.keybindingsResource); } registerEditorContribution(DefineKeybindingController.ID, DefineKeybindingController); diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index 34c4289aca3..2324e9c4b2a 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -43,7 +43,7 @@ import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle import { KeybindingsEditorInput } from 'vs/workbench/services/preferences/browser/keybindingsEditorInput'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; const SETTINGS_EDITOR_COMMAND_SEARCH = 'settings.action.search'; @@ -140,7 +140,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon constructor( @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, - @IUserDataProfilesService private readonly userDataProfileService: IUserDataProfilesService, + @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, @IPreferencesService private readonly preferencesService: IPreferencesService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @ILabelService private readonly labelService: ILabelService, diff --git a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts index 1aad47f77dc..3825c5e9caa 100644 --- a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts +++ b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts @@ -23,7 +23,7 @@ import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEdit import { RegisteredEditorPriority, IEditorResolverService } from 'vs/workbench/services/editor/common/editorResolverService'; import { ITextEditorService } from 'vs/workbench/services/textfile/common/textEditorService'; import { DEFAULT_SETTINGS_EDITOR_SETTING, FOLDER_SETTINGS_PATH, IPreferencesService, USE_SPLIT_JSON_SETTING } from 'vs/workbench/services/preferences/common/preferences'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; const schemaRegistry = Registry.as(JSONContributionRegistry.Extensions.JSONContribution); @@ -36,7 +36,7 @@ export class PreferencesContribution implements IWorkbenchContribution { @ITextModelService private readonly textModelResolverService: ITextModelService, @IPreferencesService private readonly preferencesService: IPreferencesService, @ILanguageService private readonly languageService: ILanguageService, - @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, + @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, @IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService, @IConfigurationService private readonly configurationService: IConfigurationService, @IEditorResolverService private readonly editorResolverService: IEditorResolverService, @@ -71,7 +71,7 @@ export class PreferencesContribution implements IWorkbenchContribution { }, ({ resource, options }): EditorInputWithOptions => { // Global User Settings File - if (isEqual(resource, this.userDataProfilesService.currentProfile.settingsResource)) { + if (isEqual(resource, this.userDataProfileService.currentProfile.settingsResource)) { return { editor: this.preferencesService.createSplitJsonEditorInput(ConfigurationTarget.USER_LOCAL, resource), options }; } diff --git a/src/vs/workbench/contrib/snippets/browser/configureSnippets.ts b/src/vs/workbench/contrib/snippets/browser/configureSnippets.ts index ef7217b935b..274dbaf02b5 100644 --- a/src/vs/workbench/contrib/snippets/browser/configureSnippets.ts +++ b/src/vs/workbench/contrib/snippets/browser/configureSnippets.ts @@ -18,7 +18,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { isValidBasename } from 'vs/base/common/extpath'; import { joinPath, basename } from 'vs/base/common/resources'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; namespace ISnippetPick { export function is(thing: object | undefined): thing is ISnippetPick { @@ -31,7 +31,7 @@ interface ISnippetPick extends IQuickPickItem { hint?: true; } -async function computePicks(snippetService: ISnippetsService, userDataProfilesService: IUserDataProfilesService, languageService: ILanguageService) { +async function computePicks(snippetService: ISnippetsService, userDataProfileService: IUserDataProfileService, languageService: ILanguageService) { const existing: ISnippetPick[] = []; const future: ISnippetPick[] = []; @@ -85,7 +85,7 @@ async function computePicks(snippetService: ISnippetsService, userDataProfilesSe } } - const dir = userDataProfilesService.currentProfile.snippetsHome; + const dir = userDataProfileService.currentProfile.snippetsHome; for (const languageId of languageService.getRegisteredLanguageIds()) { const label = languageService.getLanguageName(languageId); if (label && !seen.has(languageId)) { @@ -227,19 +227,19 @@ registerAction2(class ConfigureSnippets extends Action2 { const quickInputService = accessor.get(IQuickInputService); const opener = accessor.get(IOpenerService); const languageService = accessor.get(ILanguageService); - const userDataProfilesService = accessor.get(IUserDataProfilesService); + const userDataProfileService = accessor.get(IUserDataProfileService); const workspaceService = accessor.get(IWorkspaceContextService); const fileService = accessor.get(IFileService); const textFileService = accessor.get(ITextFileService); - const picks = await computePicks(snippetService, userDataProfilesService, languageService); + const picks = await computePicks(snippetService, userDataProfileService, languageService); const existing: QuickPickInput[] = picks.existing; type SnippetPick = IQuickPickItem & { uri: URI } & { scope: string }; const globalSnippetPicks: SnippetPick[] = [{ scope: nls.localize('new.global_scope', 'global'), label: nls.localize('new.global', "New Global Snippets file..."), - uri: userDataProfilesService.currentProfile.snippetsHome + uri: userDataProfileService.currentProfile.snippetsHome }]; const workspaceSnippetPicks: SnippetPick[] = []; diff --git a/src/vs/workbench/contrib/snippets/browser/snippetsService.ts b/src/vs/workbench/contrib/snippets/browser/snippetsService.ts index 9946967398e..a7d9fb27856 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippetsService.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippetsService.ts @@ -30,7 +30,7 @@ import { isStringArray } from 'vs/base/common/types'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; namespace snippetExt { @@ -178,7 +178,7 @@ class SnippetsService implements ISnippetsService { constructor( @IEnvironmentService private readonly _environmentService: IEnvironmentService, - @IUserDataProfilesService private readonly _userDataProfilesService: IUserDataProfilesService, + @IUserDataProfileService private readonly _userDataProfileService: IUserDataProfileService, @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, @ILanguageService private readonly _languageService: ILanguageService, @ILogService private readonly _logService: ILogService, @@ -350,7 +350,7 @@ class SnippetsService implements ISnippetsService { } private async _initUserSnippets(): Promise { - const userSnippetsFolder = this._userDataProfilesService.currentProfile.snippetsHome; + const userSnippetsFolder = this._userDataProfileService.currentProfile.snippetsHome; await this._fileService.createFolder(userSnippetsFolder); return await this._initFolderSnippets(SnippetSource.User, userSnippetsFolder, this._disposables); } diff --git a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts index 2fc06c7664d..3d53f842987 100644 --- a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts +++ b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts @@ -25,7 +25,7 @@ import { getMimeTypes } from 'vs/editor/common/services/languagesAssociations'; import { hash } from 'vs/base/common/hash'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { ViewContainerLocation } from 'vs/workbench/common/views'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; type TelemetryData = { mimeType: string; @@ -56,7 +56,7 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr @IKeybindingService keybindingsService: IKeybindingService, @IWorkbenchThemeService themeService: IWorkbenchThemeService, @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, - @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, + @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, @IConfigurationService configurationService: IConfigurationService, @IPaneCompositePartService paneCompositeService: IPaneCompositePartService, @ITextFileService textFileService: ITextFileService @@ -173,17 +173,17 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr } // Check for global settings file - if (isEqual(resource, this.userDataProfilesService.currentProfile.settingsResource)) { + if (isEqual(resource, this.userDataProfileService.currentProfile.settingsResource)) { return 'global-settings'; } // Check for keybindings file - if (isEqual(resource, this.userDataProfilesService.currentProfile.keybindingsResource)) { + if (isEqual(resource, this.userDataProfileService.currentProfile.keybindingsResource)) { return 'keybindings'; } // Check for snippets - if (isEqualOrParent(resource, this.userDataProfilesService.currentProfile.snippetsHome)) { + if (isEqualOrParent(resource, this.userDataProfileService.currentProfile.snippetsHome)) { return 'snippets'; } diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts index 8aeaa0c7402..cb9215711de 100644 --- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts @@ -9,7 +9,7 @@ import { localize } from 'vs/nls'; import { Action2, ISubmenuItem, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IsDevelopmentContext } from 'vs/platform/contextkey/common/contextkeys'; -import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile, IUserDataProfileService, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { WorkbenchStateContext } from 'vs/workbench/common/contextkeys'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -23,6 +23,7 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements private readonly currentProfileContext: IContextKey; constructor( + @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @IUserDataProfileManagementService private readonly userDataProfileManagementService: IUserDataProfileManagementService, @IStatusbarService private readonly statusBarService: IStatusbarService, @@ -32,7 +33,7 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements super(); this.currentProfileContext = CONTEXT_CURRENT_PROFILE.bindTo(contextKeyService); - this.currentProfileContext.set(this.userDataProfilesService.currentProfile.id); + this.currentProfileContext.set(this.userDataProfileService.currentProfile.id); this.updateStatus(); this._register(this.workspaceContextService.onDidChangeWorkbenchState(() => this.updateStatus())); @@ -51,7 +52,7 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements const that = this; const when = ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')); MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { - get title() { return localize('manageProfiles', "{0} ({1})", PROFILES_TTILE.value, that.userDataProfilesService.currentProfile.name); }, + get title() { return localize('manageProfiles', "{0} ({1})", PROFILES_TTILE.value, that.userDataProfileService.currentProfile.name); }, submenu: ManageProfilesSubMenu, group: '5_profiles', when, @@ -65,7 +66,7 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements order: 3 }); MenuRegistry.appendMenuItem(MenuId.AccountsContext, { - get title() { return localize('manageProfiles', "{0} ({1})", PROFILES_TTILE.value, that.userDataProfilesService.currentProfile.name); }, + get title() { return localize('manageProfiles', "{0} ({1})", PROFILES_TTILE.value, that.userDataProfileService.currentProfile.name); }, submenu: ManageProfilesSubMenu, group: '1_profiles', when, @@ -106,13 +107,12 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements } private async updateStatus(): Promise { - const profiles = await this.userDataProfilesService.getAllProfiles(); - if (profiles.length > 1 && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY) { + if (this.userDataProfilesService.profiles.length > 1 && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY) { this.statusBarService.addEntry({ - name: this.userDataProfilesService.currentProfile.name!, + name: this.userDataProfileService.currentProfile.name!, command: 'workbench.profiles.actions.switchProfile', - ariaLabel: localize('currentProfile', "Current Settings Profile is {0}", this.userDataProfilesService.currentProfile.name), - text: `${PROFILES_CATEGORY}: ${this.userDataProfilesService.currentProfile.name!}`, + ariaLabel: localize('currentProfile', "Current Settings Profile is {0}", this.userDataProfileService.currentProfile.name), + text: `${PROFILES_CATEGORY}: ${this.userDataProfileService.currentProfile.name!}`, }, 'status.userDataProfile', StatusbarAlignment.LEFT, 1); } } diff --git a/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts b/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts index 1d72c8ac7fa..97d88b2b3df 100644 --- a/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts +++ b/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts @@ -16,7 +16,7 @@ import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/commo import { asJson, asText, IRequestService } from 'vs/platform/request/common/request'; import { IUserDataProfileTemplate, isUserDataProfileTemplate, IUserDataProfileManagementService, IUserDataProfileWorkbenchService, PROFILES_CATEGORY, PROFILE_EXTENSION, PROFILE_FILTER, ManageProfilesSubMenu } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile, IUserDataProfileService, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IsDevelopmentContext } from 'vs/platform/contextkey/common/contextkeys'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { WorkbenchStateContext } from 'vs/workbench/common/contextkeys'; @@ -119,10 +119,11 @@ registerAction2(class RemoveProfileAction extends Action2 { async run(accessor: ServicesAccessor) { const quickInputService = accessor.get(IQuickInputService); + const userDataProfileService = accessor.get(IUserDataProfileService); const userDataProfilesService = accessor.get(IUserDataProfilesService); const userDataProfileManagementService = accessor.get(IUserDataProfileManagementService); - const profiles = (await userDataProfilesService.getAllProfiles()).filter(p => p.name !== userDataProfilesService.currentProfile.name && p.name !== userDataProfilesService.defaultProfile.name); + const profiles = userDataProfilesService.profiles.filter(p => p.name !== userDataProfileService.currentProfile.name && p.name !== userDataProfilesService.defaultProfile.name); if (profiles.length) { const pick = await quickInputService.pick(profiles.map(profile => ({ label: profile.name, profile })), { placeHolder: localize('pick profile', "Select Settings Profile") }); if (pick) { @@ -148,14 +149,14 @@ registerAction2(class SwitchProfileAction extends Action2 { async run(accessor: ServicesAccessor) { const quickInputService = accessor.get(IQuickInputService); + const userDataProfileService = accessor.get(IUserDataProfileService); const userDataProfilesService = accessor.get(IUserDataProfilesService); const userDataProfileManagementService = accessor.get(IUserDataProfileManagementService); - const profiles = await userDataProfilesService.getAllProfiles(); - if (profiles.length) { - const picks: Array = profiles.map(profile => ({ + if (userDataProfilesService.profiles) { + const picks: Array = userDataProfilesService.profiles.map(profile => ({ label: profile.name!, - description: profile.name === userDataProfilesService.currentProfile.name ? localize('current', "Current") : undefined, + description: profile.name === userDataProfileService.currentProfile.name ? localize('current', "Current") : undefined, profile })); const pick = await quickInputService.pick(picks, { placeHolder: localize('pick profile', "Select Settings Profile") }); @@ -185,9 +186,8 @@ registerAction2(class CleanupProfilesAction extends Action2 { const fileService = accessor.get(IFileService); const uriIdentityService = accessor.get(IUriIdentityService); - const allProfiles = await userDataProfilesService.getAllProfiles(); const stat = await fileService.resolve(userDataProfilesService.profilesHome); - await Promise.all((stat.children || [])?.filter(child => child.isDirectory && allProfiles.every(p => !uriIdentityService.extUri.isEqual(p.location, child.resource))) + await Promise.all((stat.children || [])?.filter(child => child.isDirectory && userDataProfilesService.profiles.every(p => !uriIdentityService.extUri.isEqual(p.location, child.resource))) .map(child => fileService.del(child.resource, { recursive: true }))); } }); diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts index 2dd345d1d19..7bcef13cecc 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -50,11 +50,11 @@ import { isCI, isMacintosh } from 'vs/base/common/platform'; import { Schemas } from 'vs/base/common/network'; import { DiskFileSystemProvider } from 'vs/workbench/services/files/electron-sandbox/diskFileSystemProvider'; import { FileUserDataProvider } from 'vs/platform/userData/common/fileUserDataProvider'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService, IUserDataProfilesService, reviveProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { UserDataProfilesNativeService } from 'vs/platform/userDataProfile/electron-sandbox/userDataProfile'; import { PolicyChannelClient } from 'vs/platform/policy/common/policyIpc'; import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; -import { revive } from 'vs/base/common/marshalling'; +import { UserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfileService'; export class DesktopMain extends Disposable { @@ -240,8 +240,10 @@ export class DesktopMain extends Disposable { serviceCollection.set(IUriIdentityService, uriIdentityService); // User Data Profiles - const userDataProfilesService = new UserDataProfilesNativeService(revive(this.configuration.profiles.default), revive(this.configuration.profiles.current), mainProcessService.getChannel('userDataProfiles'), environmentService, fileService, logService); + const userDataProfilesService = new UserDataProfilesNativeService(this.configuration.profiles.default, mainProcessService.getChannel('userDataProfiles'), environmentService, fileService, logService); serviceCollection.set(IUserDataProfilesService, userDataProfilesService); + const userDataProfileService = new UserDataProfileService(userDataProfilesService.defaultProfile, reviveProfile(this.configuration.profiles.current, userDataProfilesService.profilesHome.scheme)); + serviceCollection.set(IUserDataProfileService, userDataProfileService); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // @@ -259,7 +261,7 @@ export class DesktopMain extends Disposable { const payload = this.resolveWorkspaceInitializationPayload(environmentService); const [configurationService, storageService] = await Promise.all([ - this.createWorkspaceService(payload, environmentService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService, policyService).then(service => { + this.createWorkspaceService(payload, environmentService, userDataProfileService, fileService, remoteAgentService, uriIdentityService, logService, policyService).then(service => { // Workspace serviceCollection.set(IWorkspaceContextService, service); @@ -270,7 +272,7 @@ export class DesktopMain extends Disposable { return service; }), - this.createStorageService(payload, environmentService, userDataProfilesService, mainProcessService).then(service => { + this.createStorageService(payload, environmentService, userDataProfileService, mainProcessService).then(service => { // Storage serviceCollection.set(IStorageService, service); @@ -340,7 +342,7 @@ export class DesktopMain extends Disposable { private async createWorkspaceService( payload: IAnyWorkspaceIdentifier, environmentService: INativeWorkbenchEnvironmentService, - userDataProfilesService: IUserDataProfilesService, + userDataProfileService: IUserDataProfileService, fileService: FileService, remoteAgentService: IRemoteAgentService, uriIdentityService: IUriIdentityService, @@ -348,7 +350,7 @@ export class DesktopMain extends Disposable { policyService: IPolicyService ): Promise { const configurationCache = new ConfigurationCache([Schemas.file, Schemas.vscodeUserData] /* Cache all non native resources */, environmentService, fileService); - const workspaceService = new WorkspaceService({ remoteAuthority: environmentService.remoteAuthority, configurationCache }, environmentService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService, policyService); + const workspaceService = new WorkspaceService({ remoteAuthority: environmentService.remoteAuthority, configurationCache }, environmentService, userDataProfileService, fileService, remoteAgentService, uriIdentityService, logService, policyService); try { await workspaceService.initialize(payload); @@ -361,8 +363,8 @@ export class DesktopMain extends Disposable { } } - private async createStorageService(payload: IAnyWorkspaceIdentifier, environmentService: INativeWorkbenchEnvironmentService, userDataProfilesService: IUserDataProfilesService, mainProcessService: IMainProcessService): Promise { - const storageService = new NativeStorageService(payload, mainProcessService, userDataProfilesService, environmentService); + private async createStorageService(payload: IAnyWorkspaceIdentifier, environmentService: INativeWorkbenchEnvironmentService, userDataProfileService: IUserDataProfileService, mainProcessService: IMainProcessService): Promise { + const storageService = new NativeStorageService(payload, mainProcessService, userDataProfileService, environmentService); try { await storageService.initialize(); diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 6855bf0628d..81a8d2b63a1 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -40,7 +40,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService'; import { isUndefined } from 'vs/base/common/types'; import { localize } from 'vs/nls'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; class Workspace extends BaseWorkspace { @@ -102,7 +102,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat constructor( { remoteAuthority, configurationCache }: { remoteAuthority?: string; configurationCache: IConfigurationCache }, environmentService: IWorkbenchEnvironmentService, - userDataProfilesService: IUserDataProfilesService, + userDataProfileService: IUserDataProfileService, fileService: IFileService, remoteAgentService: IRemoteAgentService, uriIdentityService: IUriIdentityService, @@ -123,7 +123,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat this.logService = logService; this._configuration = new Configuration(this.defaultConfiguration.configurationModel, this.policyConfiguration.configurationModel, new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), this.workspace); this.cachedFolderConfigs = new ResourceMap(); - this.localUserConfiguration = this._register(new UserConfiguration(userDataProfilesService.currentProfile, remoteAuthority ? LOCAL_MACHINE_SCOPES : undefined, fileService, uriIdentityService, logService)); + this.localUserConfiguration = this._register(new UserConfiguration(userDataProfileService.currentProfile, remoteAuthority ? LOCAL_MACHINE_SCOPES : undefined, fileService, uriIdentityService, logService)); this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration))); if (remoteAuthority) { const remoteUserConfiguration = this.remoteUserConfiguration = this._register(new RemoteUserConfiguration(remoteAuthority, configurationCache, fileService, uriIdentityService, remoteAgentService)); diff --git a/src/vs/workbench/services/configuration/common/configurationEditingService.ts b/src/vs/workbench/services/configuration/common/configurationEditingService.ts index 1bf0adcc9e3..0ec0b7b1d19 100644 --- a/src/vs/workbench/services/configuration/common/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditingService.ts @@ -28,7 +28,7 @@ import { IReference } from 'vs/base/common/lifecycle'; import { Range } from 'vs/editor/common/core/range'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Selection } from 'vs/editor/common/core/selection'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; export const enum ConfigurationEditingErrorCode { @@ -153,7 +153,7 @@ export class ConfigurationEditingService { constructor( @IConfigurationService private readonly configurationService: IConfigurationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, + @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, @IFileService private readonly fileService: IFileService, @ITextModelService private readonly textModelResolverService: ITextModelService, @ITextFileService private readonly textFileService: ITextFileService, @@ -614,9 +614,9 @@ export class ConfigurationEditingService { private getConfigurationFileResource(target: EditableConfigurationTarget, standAloneConfigurationKey: string | undefined, relativePath: string, resource: URI | null | undefined): URI | null { if (target === EditableConfigurationTarget.USER_LOCAL) { if (standAloneConfigurationKey === TASKS_CONFIGURATION_KEY) { - return this.userDataProfilesService.currentProfile.tasksResource; + return this.userDataProfileService.currentProfile.tasksResource; } else { - return this.userDataProfilesService.currentProfile.settingsResource; + return this.userDataProfileService.currentProfile.settingsResource; } } if (target === EditableConfigurationTarget.USER_REMOTE) { diff --git a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts index 2ed465e18a6..d2026aacd49 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts @@ -39,11 +39,12 @@ import { joinPath } from 'vs/base/common/resources'; import { VSBuffer } from 'vs/base/common/buffer'; import { RemoteAgentService } from 'vs/workbench/services/remote/browser/remoteAgentService'; import { getSingleFolderWorkspaceIdentifier } from 'vs/workbench/services/workspaces/browser/workspaces'; -import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { DefaultOptions, IUserDataProfileService, toUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { hash } from 'vs/base/common/hash'; import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; +import { UserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfileService'; const ROOT = URI.file('tests').with({ scheme: 'vscode-tests' }); @@ -57,7 +58,7 @@ export class ConfigurationCache implements IConfigurationCache { suite('ConfigurationEditingService', () => { let instantiationService: TestInstantiationService; - let userDataProfilesService: IUserDataProfilesService; + let userDataProfileService: IUserDataProfileService; let environmentService: IWorkbenchEnvironmentService; let fileService: IFileService; let workspaceService: WorkspaceService; @@ -108,12 +109,13 @@ suite('ConfigurationEditingService', () => { environmentService = TestEnvironmentService; environmentService.policyFile = joinPath(workspaceFolder, 'policies.json'); instantiationService.stub(IEnvironmentService, environmentService); - userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService)); + const profile = toUserDataProfile('temp', environmentService.userRoamingDataHome, DefaultOptions, true); + userDataProfileService = new UserDataProfileService(profile, profile); const remoteAgentService = disposables.add(instantiationService.createInstance(RemoteAgentService, null)); disposables.add(fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, logService)))); instantiationService.stub(IFileService, fileService); instantiationService.stub(IRemoteAgentService, remoteAgentService); - workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new FilePolicyService(environmentService.policyFile, fileService, logService))); + workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfileService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new FilePolicyService(environmentService.policyFile, fileService, logService))); await workspaceService.initialize({ id: hash(workspaceFolder.toString()).toString(16), uri: workspaceFolder @@ -153,7 +155,7 @@ suite('ConfigurationEditingService', () => { }); test('errors cases - invalid configuration', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString(',,,,,,,,,,,,,,')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString(',,,,,,,,,,,,,,')); try { await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: 'value' }, { donotNotifyError: true }); } catch (error) { @@ -217,43 +219,43 @@ suite('ConfigurationEditingService', () => { test('write policy setting - when not set', async () => { await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.policySetting', value: 'value' }, { donotNotifyError: true }); - const contents = await fileService.readFile(userDataProfilesService.currentProfile.settingsResource); + const contents = await fileService.readFile(userDataProfileService.currentProfile.settingsResource); const parsed = json.parse(contents.value.toString()); assert.strictEqual(parsed['configurationEditing.service.policySetting'], 'value'); }); test('write one setting - empty file', async () => { await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: 'value' }); - const contents = await fileService.readFile(userDataProfilesService.currentProfile.settingsResource); + const contents = await fileService.readFile(userDataProfileService.currentProfile.settingsResource); const parsed = json.parse(contents.value.toString()); assert.strictEqual(parsed['configurationEditing.service.testSetting'], 'value'); }); test('write one setting - existing file', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "my.super.setting": "my.super.value" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "my.super.setting": "my.super.value" }')); await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: 'value' }); - const contents = await fileService.readFile(userDataProfilesService.currentProfile.settingsResource); + const contents = await fileService.readFile(userDataProfileService.currentProfile.settingsResource); const parsed = json.parse(contents.value.toString()); assert.strictEqual(parsed['configurationEditing.service.testSetting'], 'value'); assert.strictEqual(parsed['my.super.setting'], 'my.super.value'); }); test('remove an existing setting - existing file', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "my.super.setting": "my.super.value", "configurationEditing.service.testSetting": "value" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "my.super.setting": "my.super.value", "configurationEditing.service.testSetting": "value" }')); await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: undefined }); - const contents = await fileService.readFile(userDataProfilesService.currentProfile.settingsResource); + const contents = await fileService.readFile(userDataProfileService.currentProfile.settingsResource); const parsed = json.parse(contents.value.toString()); assert.deepStrictEqual(Object.keys(parsed), ['my.super.setting']); assert.strictEqual(parsed['my.super.setting'], 'my.super.value'); }); test('remove non existing setting - existing file', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "my.super.setting": "my.super.value" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "my.super.setting": "my.super.value" }')); await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: undefined }); - const contents = await fileService.readFile(userDataProfilesService.currentProfile.settingsResource); + const contents = await fileService.readFile(userDataProfileService.currentProfile.settingsResource); const parsed = json.parse(contents.value.toString()); assert.deepStrictEqual(Object.keys(parsed), ['my.super.setting']); assert.strictEqual(parsed['my.super.setting'], 'my.super.value'); @@ -264,7 +266,7 @@ suite('ConfigurationEditingService', () => { const value = { 'configurationEditing.service.testSetting': 'overridden value' }; await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key, value }); - const contents = await fileService.readFile(userDataProfilesService.currentProfile.settingsResource); + const contents = await fileService.readFile(userDataProfileService.currentProfile.settingsResource); const parsed = json.parse(contents.value.toString()); assert.deepStrictEqual(parsed[key], value); }); diff --git a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts index b83a65216b5..18c28ba27a5 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts @@ -44,10 +44,11 @@ import { RemoteAgentService } from 'vs/workbench/services/remote/browser/remoteA import { RemoteAuthorityResolverService } from 'vs/platform/remote/browser/remoteAuthorityResolverService'; import { hash } from 'vs/base/common/hash'; import { TestProductService } from 'vs/workbench/test/common/workbenchTestServices'; -import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { DefaultOptions, IUserDataProfileService, toUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { NullPolicyService } from 'vs/platform/policy/common/policy'; import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; +import { UserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfileService'; function convertToWorkspacePayload(folder: URI): ISingleFolderWorkspaceIdentifier { return { @@ -65,6 +66,11 @@ export class ConfigurationCache implements IConfigurationCache { const ROOT = URI.file('tests').with({ scheme: 'vscode-tests' }); +function aUserDataProfileService(environmentService: IEnvironmentService): IUserDataProfileService { + const profile = toUserDataProfile('temp', environmentService.userRoamingDataHome, DefaultOptions, true); + return new UserDataProfileService(profile, profile); +} + suite('WorkspaceContextService - Folder', () => { const folderName = 'Folder A'; @@ -83,7 +89,7 @@ suite('WorkspaceContextService - Folder', () => { const environmentService = TestEnvironmentService; fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); + testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, aUserDataProfileService(environmentService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); await (testObject).initialize(convertToWorkspacePayload(folder)); }); @@ -123,7 +129,7 @@ suite('WorkspaceContextService - Folder', () => { const environmentService = TestEnvironmentService; fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); + const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, aUserDataProfileService(environmentService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); await (testObject).initialize(convertToWorkspacePayload(folder)); const actual = testObject.getWorkspaceFolder(joinPath(folder, 'a')); @@ -143,7 +149,7 @@ suite('WorkspaceContextService - Folder', () => { const environmentService = TestEnvironmentService; fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); + const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, aUserDataProfileService(environmentService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); await (testObject).initialize(convertToWorkspacePayload(folder)); @@ -190,7 +196,7 @@ suite('WorkspaceContextService - Workspace', () => { const remoteAgentService = disposables.add(instantiationService.createInstance(RemoteAgentService, null)); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService), fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); + testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, aUserDataProfileService(environmentService), fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IConfigurationService, testObject); @@ -248,7 +254,7 @@ suite('WorkspaceContextService - Workspace Editing', () => { const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService), fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); + testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, aUserDataProfileService(environmentService), fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); instantiationService.stub(IFileService, fileService); instantiationService.stub(IWorkspaceContextService, testObject); @@ -446,7 +452,7 @@ suite('WorkspaceContextService - Workspace Editing', () => { suite('WorkspaceService - Initialization', () => { - let configResource: URI, testObject: WorkspaceService, fileService: IFileService, environmentService: BrowserWorkbenchEnvironmentService, userDataProfilesService: IUserDataProfilesService; + let configResource: URI, testObject: WorkspaceService, fileService: IFileService, environmentService: BrowserWorkbenchEnvironmentService, userDataProfileService: IUserDataProfileService; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); const disposables = new DisposableStore(); @@ -490,9 +496,9 @@ suite('WorkspaceService - Initialization', () => { environmentService = TestEnvironmentService; const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); - userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService)); + userDataProfileService = instantiationService.stub(IUserDataProfileService, aUserDataProfileService(environmentService)); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); + testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfileService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); instantiationService.stub(IFileService, fileService); instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IConfigurationService, testObject); @@ -508,7 +514,7 @@ suite('WorkspaceService - Initialization', () => { (isMacintosh ? test.skip : test)('initialize a folder workspace from an empty workspace with no configuration changes', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); await testObject.reloadConfiguration(); const target = sinon.spy(); @@ -533,7 +539,7 @@ suite('WorkspaceService - Initialization', () => { (isMacintosh ? test.skip : test)('initialize a folder workspace from an empty workspace with configuration changes', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); await testObject.reloadConfiguration(); const target = sinon.spy(); @@ -560,7 +566,7 @@ suite('WorkspaceService - Initialization', () => { (isMacintosh ? test.skip : test)('initialize a multi root workspace from an empty workspace with no configuration changes', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); await testObject.reloadConfiguration(); const target = sinon.spy(); @@ -583,7 +589,7 @@ suite('WorkspaceService - Initialization', () => { (isMacintosh ? test.skip : test)('initialize a multi root workspace from an empty workspace with configuration changes', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); await testObject.reloadConfiguration(); const target = sinon.spy(); @@ -610,7 +616,7 @@ suite('WorkspaceService - Initialization', () => { (isMacintosh ? test.skip : test)('initialize a folder workspace from a folder workspace with no configuration changes', async () => { await testObject.initialize(convertToWorkspacePayload(joinPath(ROOT, 'a'))); - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); await testObject.reloadConfiguration(); const target = sinon.spy(); testObject.onDidChangeWorkbenchState(target); @@ -676,7 +682,7 @@ suite('WorkspaceService - Initialization', () => { suite('WorkspaceConfigurationService - Folder', () => { - let testObject: WorkspaceService, workspaceService: WorkspaceService, fileService: IFileService, environmentService: IWorkbenchEnvironmentService, userDataProfilesService: IUserDataProfilesService; + let testObject: WorkspaceService, workspaceService: WorkspaceService, fileService: IFileService, environmentService: IWorkbenchEnvironmentService, userDataProfileService: IUserDataProfileService; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); const disposables: DisposableStore = new DisposableStore(); @@ -750,8 +756,8 @@ suite('WorkspaceConfigurationService - Folder', () => { const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService)); - workspaceService = testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new FilePolicyService(environmentService.policyFile, fileService, logService))); + userDataProfileService = instantiationService.stub(IUserDataProfileService, aUserDataProfileService(environmentService)); + workspaceService = testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfileService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new FilePolicyService(environmentService.policyFile, fileService, logService))); instantiationService.stub(IFileService, fileService); instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IConfigurationService, testObject); @@ -771,13 +777,13 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('globals override defaults', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.testSetting'), 'userValue'); }); test('globals', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('testworkbench.editor.tabs'), true); }); @@ -789,21 +795,21 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('workspace settings override user settings', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.testSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.testSetting'), 'workspaceValue'); }); test('machine overridable settings override user Settings', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineOverridableSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineOverridableSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineOverridableSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.machineOverridableSetting'), 'workspaceValue'); }); test('workspace settings override user settings after defaults are registered ', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.newSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.newSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.newSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); configurationRegistry.registerConfiguration({ @@ -820,7 +826,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('machine overridable settings override user settings after defaults are registered ', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.newMachineOverridableSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.newMachineOverridableSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.newMachineOverridableSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); configurationRegistry.registerConfiguration({ @@ -838,7 +844,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('application settings are not read from workspace', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -847,7 +853,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('application settings are not read from workspace when workspace folder uri is passed', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -856,7 +862,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('machine settings are not read from workspace', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -865,7 +871,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('machine settings are not read from workspace when workspace folder uri is passed', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -874,7 +880,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('get application scope settings are not loaded after defaults are registered', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting-2": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting-2": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.applicationSetting-2": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -899,7 +905,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('get application scope settings are not loaded after defaults are registered when workspace folder uri is passed', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting-3": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting-3": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.applicationSetting-3": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -924,7 +930,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('get machine scope settings are not loaded after defaults are registered', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting-2": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting-2": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineSetting-2": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -949,7 +955,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('get machine scope settings are not loaded after defaults are registered when workspace folder uri is passed', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting-3": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting-3": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineSetting-3": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -985,7 +991,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('policy settings when policy value is not set', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.policySetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.policySetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.policySetting": "workspaceValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.policySetting'), 'workspaceValue'); @@ -993,7 +999,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('reload configuration emits events after global configuraiton changes', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); const target = sinon.spy(); testObject.onDidChangeConfiguration(target); await testObject.reloadConfiguration(); @@ -1009,7 +1015,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('reload configuration should not emit event if no changes', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.testSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); const target = sinon.spy(); @@ -1033,7 +1039,7 @@ suite('WorkspaceConfigurationService - Folder', () => { assert.strictEqual(actual.workspaceFolderValue, undefined); assert.strictEqual(actual.value, 'isSet'); - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); await testObject.reloadConfiguration(); actual = testObject.inspect('configurationService.folder.testSetting'); assert.strictEqual(actual.defaultValue, 'isSet'); @@ -1059,7 +1065,7 @@ suite('WorkspaceConfigurationService - Folder', () => { assert.deepStrictEqual(actual.workspace, []); assert.deepStrictEqual(actual.workspaceFolder, []); - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); await testObject.reloadConfiguration(); actual = testObject.keys(); assert.ok(actual.default.indexOf('configurationService.folder.testSetting') !== -1); @@ -1214,7 +1220,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('creating workspace settings', () => runWithFakedTimers({ useFakeTimers: true }, async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); await testObject.reloadConfiguration(); await new Promise((c, e) => { const disposable = testObject.onDidChangeConfiguration(e => { @@ -1228,7 +1234,7 @@ suite('WorkspaceConfigurationService - Folder', () => { })); test('deleting workspace settings', () => runWithFakedTimers({ useFakeTimers: true }, async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); const workspaceSettingsResource = joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'); await fileService.writeFile(workspaceSettingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1243,7 +1249,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('restricted setting is read from workspace when workspace is trusted', async () => { testObject.updateWorkspaceTrust(true); - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1259,7 +1265,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('restricted setting is not read from workspace when workspace is changed to trusted', async () => { testObject.updateWorkspaceTrust(true); - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1277,7 +1283,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('change event is triggered when workspace is changed to untrusted', async () => { testObject.updateWorkspaceTrust(true); - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1292,7 +1298,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('restricted setting is not read from workspace when workspace is not trusted', async () => { testObject.updateWorkspaceTrust(false); - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1308,7 +1314,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('restricted setting is read when workspace is changed to trusted', async () => { testObject.updateWorkspaceTrust(false); - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1326,7 +1332,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('change event is triggered when workspace is changed to trusted', async () => { testObject.updateWorkspaceTrust(false); - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1339,7 +1345,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('adding an restricted setting triggers change event', () => runWithFakedTimers({ useFakeTimers: true }, async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); testObject.updateWorkspaceTrust(false); const promise = Event.toPromise(testObject.onDidChangeRestrictedSettings); @@ -1350,7 +1356,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('remove an unregistered setting', async () => { const key = 'configurationService.folder.unknownSetting'; - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.unknownSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.unknownSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.unknownSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1365,7 +1371,7 @@ suite('WorkspaceConfigurationService - Folder', () => { suite('WorkspaceConfigurationService-Multiroot', () => { - let workspaceContextService: IWorkspaceContextService, jsonEditingServce: IJSONEditingService, testObject: WorkspaceService, fileService: IFileService, environmentService: BrowserWorkbenchEnvironmentService, userDataProfilesService: IUserDataProfilesService; + let workspaceContextService: IWorkspaceContextService, jsonEditingServce: IJSONEditingService, testObject: WorkspaceService, fileService: IFileService, environmentService: BrowserWorkbenchEnvironmentService, userDataProfileService: IUserDataProfileService; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); const disposables = new DisposableStore(); @@ -1441,8 +1447,8 @@ suite('WorkspaceConfigurationService-Multiroot', () => { const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService)); - const workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); + userDataProfileService = instantiationService.stub(IUserDataProfileService, aUserDataProfileService(environmentService)); + const workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfileService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); instantiationService.stub(IFileService, fileService); instantiationService.stub(IWorkspaceContextService, workspaceService); @@ -1464,7 +1470,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { teardown(() => disposables.clear()); test('application settings are not read from workspace', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.applicationSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1473,7 +1479,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace when folder is passed', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.applicationSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1482,7 +1488,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('machine settings are not read from workspace', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.machineSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1491,7 +1497,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('machine settings are not read from workspace when folder is passed', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.machineSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1500,7 +1506,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('get application scope settings are not loaded after defaults are registered', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.newSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1525,7 +1531,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('get application scope settings are not loaded after defaults are registered when workspace folder is passed', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newSetting-2": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newSetting-2": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.newSetting-2': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1550,7 +1556,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('workspace settings override user settings after defaults are registered for machine overridable settings ', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newMachineOverridableSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newMachineOverridableSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.newMachineOverridableSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1576,7 +1582,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace folder', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1585,7 +1591,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace folder when workspace folder is passed', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1594,7 +1600,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('machine settings are not read from workspace folder', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1603,7 +1609,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('machine settings are not read from workspace folder when workspace folder is passed', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1612,7 +1618,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace folder after defaults are registered', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testNewApplicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testNewApplicationSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.testNewApplicationSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1637,7 +1643,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace folder after defaults are registered', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testNewMachineSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testNewMachineSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.testNewMachineSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1730,7 +1736,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { assert.strictEqual(actual.workspaceFolderValue, undefined); assert.strictEqual(actual.value, 'isSet'); - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testResourceSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testResourceSetting": "userValue" }')); await testObject.reloadConfiguration(); actual = testObject.inspect('configurationService.workspace.testResourceSetting'); assert.strictEqual(actual.defaultValue, 'isSet'); @@ -1985,7 +1991,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { test('restricted setting is read from workspace folders when workspace is trusted', async () => { testObject.updateWorkspaceTrust(true); - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting1": "userValue", "configurationService.workspace.testRestrictedSetting2": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting1": "userValue", "configurationService.workspace.testRestrictedSetting2": "userValue" }')); await jsonEditingServce.write((workspaceContextService.getWorkspace().configuration!), [{ path: ['settings'], value: { 'configurationService.workspace.testRestrictedSetting1': 'workspaceValue' } }], true); await fileService.writeFile(joinPath(testObject.getWorkspace().folders[1].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting2": "workspaceFolder2Value" }')); await testObject.reloadConfiguration(); @@ -2005,7 +2011,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { test('restricted setting is not read from workspace when workspace is not trusted', async () => { testObject.updateWorkspaceTrust(false); - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting1": "userValue", "configurationService.workspace.testRestrictedSetting2": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting1": "userValue", "configurationService.workspace.testRestrictedSetting2": "userValue" }')); await jsonEditingServce.write((workspaceContextService.getWorkspace().configuration!), [{ path: ['settings'], value: { 'configurationService.workspace.testRestrictedSetting1': 'workspaceValue' } }], true); await fileService.writeFile(joinPath(testObject.getWorkspace().folders[1].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting2": "workspaceFolder2Value" }')); await testObject.reloadConfiguration(); @@ -2024,7 +2030,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { test('remove an unregistered setting', async () => { const key = 'configurationService.workspace.unknownSetting'; - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.unknownSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.unknownSetting": "userValue" }')); await jsonEditingServce.write((workspaceContextService.getWorkspace().configuration!), [{ path: ['settings'], value: { 'configurationService.workspace.unknownSetting': 'workspaceValue' } }], true); await fileService.writeFile(joinPath(workspaceContextService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.workspace.unknownSetting": "workspaceFolderValue1" }')); await fileService.writeFile(joinPath(workspaceContextService.getWorkspace().folders[1].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.workspace.unknownSetting": "workspaceFolderValue2" }')); @@ -2050,7 +2056,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { let testObject: WorkspaceService, folder: URI, machineSettingsResource: URI, remoteSettingsResource: URI, fileSystemProvider: InMemoryFileSystemProvider, resolveRemoteEnvironment: () => void, - instantiationService: TestInstantiationService, fileService: IFileService, environmentService: BrowserWorkbenchEnvironmentService, userDataProfilesService: IUserDataProfilesService; + instantiationService: TestInstantiationService, fileService: IFileService, environmentService: BrowserWorkbenchEnvironmentService, userDataProfileService: IUserDataProfileService; const remoteAuthority = 'configuraiton-tests'; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); const disposables = new DisposableStore(); @@ -2103,8 +2109,8 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { const remoteAgentService = instantiationService.stub(IRemoteAgentService, >{ getEnvironment: () => remoteEnvironmentPromise }); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); const configurationCache: IConfigurationCache = { read: () => Promise.resolve(''), write: () => Promise.resolve(), remove: () => Promise.resolve(), needsCaching: () => false }; - userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService)); - testObject = disposables.add(new WorkspaceService({ configurationCache, remoteAuthority }, environmentService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); + userDataProfileService = instantiationService.stub(IUserDataProfileService, aUserDataProfileService(environmentService)); + testObject = disposables.add(new WorkspaceService({ configurationCache, remoteAuthority }, environmentService, userDataProfileService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IConfigurationService, testObject); instantiationService.stub(IEnvironmentService, environmentService); @@ -2190,7 +2196,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { }); test('machine settings in local user settings does not override defaults', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.machineSetting": "globalValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.machineSetting": "globalValue" }')); registerRemoteFileSystemProvider(); resolveRemoteEnvironment(); await initialize(); @@ -2198,7 +2204,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { }); test('machine overridable settings in local user settings does not override defaults', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.machineOverridableSetting": "globalValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.machineOverridableSetting": "globalValue" }')); registerRemoteFileSystemProvider(); resolveRemoteEnvironment(); await initialize(); @@ -2233,7 +2239,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { }); test('machine settings in local user settings does not override defaults after defalts are registered ', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.newMachineSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.newMachineSetting": "userValue" }')); registerRemoteFileSystemProvider(); resolveRemoteEnvironment(); await initialize(); @@ -2252,7 +2258,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { }); test('machine overridable settings in local user settings does not override defaults after defaults are registered ', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.newMachineOverridableSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.newMachineOverridableSetting": "userValue" }')); registerRemoteFileSystemProvider(); resolveRemoteEnvironment(); await initialize(); diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts index ef4da6db30d..5622a25d528 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts @@ -31,7 +31,7 @@ export class ExtensionManagementServerService implements IExtensionManagementSer ) { const remoteAgentConnection = remoteAgentService.getConnection(); if (remoteAgentConnection) { - const extensionManagementService = new ExtensionManagementChannelClient(remoteAgentConnection.getChannel('extensions'), undefined); + const extensionManagementService = new ExtensionManagementChannelClient(remoteAgentConnection.getChannel('extensions')); this.remoteExtensionManagementServer = { id: 'remote', extensionManagementService, diff --git a/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService.ts b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService.ts index 8379d5f86d6..292e2bfe2d2 100644 --- a/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService.ts @@ -15,7 +15,8 @@ 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 { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { URI } from 'vs/base/common/uri'; export class ExtensionManagementServerService implements IExtensionManagementServerService { @@ -30,10 +31,17 @@ export class ExtensionManagementServerService implements IExtensionManagementSer @ISharedProcessService sharedProcessService: ISharedProcessService, @IRemoteAgentService remoteAgentService: IRemoteAgentService, @ILabelService labelService: ILabelService, - @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, + @IUserDataProfileService userDataProfileService: IUserDataProfileService, @IInstantiationService instantiationService: IInstantiationService, ) { - const localExtensionManagementService = new ExtensionManagementChannelClient(sharedProcessService.getChannel('extensions'), userDataProfilesService); + const localExtensionManagementService = new class extends ExtensionManagementChannelClient { + constructor() { + super(sharedProcessService.getChannel('extensions')); + } + protected override getExtensionsProfileResource(): URI | undefined { + return userDataProfileService.currentProfile.extensionsResource; + } + }; this._localExtensionManagementServer = { extensionManagementService: localExtensionManagementService, id: 'local', label: localize('local', "Local") }; const remoteAgentConnection = remoteAgentService.getConnection(); diff --git a/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts index 3b502201ed4..f431cac5378 100644 --- a/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts @@ -35,7 +35,7 @@ export class NativeRemoteExtensionManagementService extends ExtensionManagementC @INativeWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService, @IExtensionManifestPropertiesService private readonly extensionManifestPropertiesService: IExtensionManifestPropertiesService, ) { - super(channel, undefined); + super(channel); } override async install(vsix: URI, options?: InstallVSIXOptions): Promise { diff --git a/src/vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner.ts index a38a74255db..c6602496838 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner.ts @@ -15,7 +15,7 @@ import { localize } from 'vs/nls'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { timeout } from 'vs/base/common/async'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; export class CachedExtensionScanner { @@ -27,7 +27,7 @@ export class CachedExtensionScanner { @INotificationService private readonly _notificationService: INotificationService, @IHostService private readonly _hostService: IHostService, @IExtensionsScannerService private readonly _extensionsScannerService: IExtensionsScannerService, - @IUserDataProfilesService private readonly _userDataProfilesService: IUserDataProfilesService, + @IUserDataProfileService private readonly _userDataProfileService: IUserDataProfileService, @ILogService private readonly _logService: ILogService, ) { this.scannedExtensions = new Promise((resolve, reject) => { @@ -55,7 +55,7 @@ export class CachedExtensionScanner { const language = platform.language; const [scannedSystemExtensions, scannedUserExtensions] = await Promise.all([ this._extensionsScannerService.scanSystemExtensions({ language, useCache: true, checkControlFile: true }), - this._extensionsScannerService.scanUserExtensions({ language, profileLocation: this._userDataProfilesService.currentProfile.extensionsResource, useCache: true })]); + this._extensionsScannerService.scanUserExtensions({ language, profileLocation: this._userDataProfileService.currentProfile.extensionsResource, useCache: true })]); const scannedDevelopedExtensions = await this._extensionsScannerService.scanExtensionsUnderDevelopment({ language }, [...scannedSystemExtensions, ...scannedUserExtensions]); const system = scannedSystemExtensions.map(e => toExtensionDescription(e, false)); const user = scannedUserExtensions.map(e => toExtensionDescription(e, false)); diff --git a/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts b/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts index 7dc22a8528d..25deefb3839 100644 --- a/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts +++ b/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts @@ -19,7 +19,8 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { migrateExtensionStorage } from 'vs/workbench/services/extensions/common/extensionStorageMigration'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService, IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { UserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfileService'; suite('ExtensionStorageMigration', () => { @@ -36,7 +37,8 @@ suite('ExtensionStorageMigration', () => { fileService.registerProvider(ROOT.scheme, disposables.add(new InMemoryFileSystemProvider())); instantiationService.stub(IFileService, fileService); const environmentService = instantiationService.stub(IEnvironmentService, >{ userRoamingDataHome: ROOT, workspaceStorageHome }); - instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, new NullLogService())); + const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, environmentService, fileService, new NullLogService())); + instantiationService.stub(IUserDataProfileService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService.defaultProfile)); instantiationService.stub(IExtensionStorageService, instantiationService.createInstance(ExtensionStorageService)); }); diff --git a/src/vs/workbench/services/keybinding/browser/keybindingService.ts b/src/vs/workbench/services/keybinding/browser/keybindingService.ts index 97f2d4805d8..67ef5b5ec6a 100644 --- a/src/vs/workbench/services/keybinding/browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/browser/keybindingService.ts @@ -51,7 +51,7 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { dirname } from 'vs/base/common/resources'; import { getAllUnboundCommands } from 'vs/workbench/services/keybinding/browser/unboundCommands'; import { UserSettingsLabelProvider } from 'vs/base/common/keybindingLabels'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; interface ContributedKeyBinding { command: string; @@ -191,7 +191,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { @ICommandService commandService: ICommandService, @ITelemetryService telemetryService: ITelemetryService, @INotificationService notificationService: INotificationService, - @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, + @IUserDataProfileService userDataProfileService: IUserDataProfileService, @IConfigurationService configurationService: IConfigurationService, @IHostService private readonly hostService: IHostService, @IExtensionService extensionService: IExtensionService, @@ -224,7 +224,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { this._cachedResolver = null; - this.userKeybindings = this._register(new UserKeybindings(userDataProfilesService.currentProfile.keybindingsResource, fileService, logService)); + this.userKeybindings = this._register(new UserKeybindings(userDataProfileService.currentProfile.keybindingsResource, fileService, logService)); this.userKeybindings.initialize().then(() => { if (this.userKeybindings.keybindings.length) { this.updateResolver(); diff --git a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts index 22d93a2fb09..732b38857de 100644 --- a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts +++ b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts @@ -25,7 +25,7 @@ import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybindin import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; export const IKeybindingEditingService = createDecorator('keybindingEditingService'); @@ -47,14 +47,14 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding public _serviceBrand: undefined; private queue: Queue; - private resource: URI = this.userDataProfilesService.currentProfile.keybindingsResource; + private resource: URI = this.userDataProfileService.currentProfile.keybindingsResource; constructor( @ITextModelService private readonly textModelResolverService: ITextModelService, @ITextFileService private readonly textFileService: ITextFileService, @IFileService private readonly fileService: IFileService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService + @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService ) { super(); this.queue = new Queue(); diff --git a/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts b/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts index 6eb6eba0fe0..1c2906db471 100644 --- a/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts +++ b/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts @@ -28,7 +28,8 @@ import { joinPath } from 'vs/base/common/resources'; import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { VSBuffer } from 'vs/base/common/buffer'; -import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { DefaultOptions, IUserDataProfileService, toUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { UserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfileService'; interface Modifiers { metaKey?: boolean; @@ -45,7 +46,7 @@ suite('KeybindingsEditing', () => { let instantiationService: TestInstantiationService; let fileService: IFileService; let environmentService: IEnvironmentService; - let userDataProfilesService: IUserDataProfilesService; + let userDataProfileService: IUserDataProfileService; let testObject: KeybindingsEditingService; setup(async () => { @@ -64,7 +65,8 @@ suite('KeybindingsEditing', () => { const configService = new TestConfigurationService(); configService.setUserConfiguration('files', { 'eol': '\n' }); - userDataProfilesService = new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService); + const profile = toUserDataProfile('temp', environmentService.userRoamingDataHome, DefaultOptions, true); + userDataProfileService = new UserDataProfileService(profile, profile); instantiationService = workbenchInstantiationService({ fileService: () => fileService, @@ -78,7 +80,7 @@ suite('KeybindingsEditing', () => { teardown(() => disposables.clear()); test('errors cases - parse errors', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.keybindingsResource, VSBuffer.fromString(',,,,,,,,,,,,,,')); + await fileService.writeFile(userDataProfileService.currentProfile.keybindingsResource, VSBuffer.fromString(',,,,,,,,,,,,,,')); try { await testObject.editKeybinding(aResolvedKeybindingItem({ firstPart: { keyCode: KeyCode.Escape } }), 'alt+c', undefined); assert.fail('Should fail with parse errors'); @@ -88,7 +90,7 @@ suite('KeybindingsEditing', () => { }); test('errors cases - parse errors 2', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.keybindingsResource, VSBuffer.fromString('[{"key": }]')); + await fileService.writeFile(userDataProfileService.currentProfile.keybindingsResource, VSBuffer.fromString('[{"key": }]')); try { await testObject.editKeybinding(aResolvedKeybindingItem({ firstPart: { keyCode: KeyCode.Escape } }), 'alt+c', undefined); assert.fail('Should fail with parse errors'); @@ -105,7 +107,7 @@ suite('KeybindingsEditing', () => { }); test('errors cases - did not find an array', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.keybindingsResource, VSBuffer.fromString('{"key": "alt+c", "command": "hello"}')); + await fileService.writeFile(userDataProfileService.currentProfile.keybindingsResource, VSBuffer.fromString('{"key": "alt+c", "command": "hello"}')); try { await testObject.editKeybinding(aResolvedKeybindingItem({ firstPart: { keyCode: KeyCode.Escape } }), 'alt+c', undefined); assert.fail('Should fail'); @@ -115,7 +117,7 @@ suite('KeybindingsEditing', () => { }); test('edit a default keybinding to an empty file', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.keybindingsResource, VSBuffer.fromString('')); + await fileService.writeFile(userDataProfileService.currentProfile.keybindingsResource, VSBuffer.fromString('')); const expected: IUserFriendlyKeybinding[] = [{ key: 'alt+c', command: 'a' }, { key: 'escape', command: '-a' }]; await testObject.editKeybinding(aResolvedKeybindingItem({ firstPart: { keyCode: KeyCode.Escape }, command: 'a' }), 'alt+c', undefined); assert.deepStrictEqual(await getUserKeybindings(), expected); @@ -245,11 +247,11 @@ suite('KeybindingsEditing', () => { }); async function writeToKeybindingsFile(...keybindings: IUserFriendlyKeybinding[]): Promise { - await fileService.writeFile(userDataProfilesService.currentProfile.keybindingsResource, VSBuffer.fromString(JSON.stringify(keybindings || []))); + await fileService.writeFile(userDataProfileService.currentProfile.keybindingsResource, VSBuffer.fromString(JSON.stringify(keybindings || []))); } async function getUserKeybindings(): Promise { - return json.parse((await fileService.readFile(userDataProfilesService.currentProfile.keybindingsResource)).value.toString()); + return json.parse((await fileService.readFile(userDataProfileService.currentProfile.keybindingsResource)).value.toString()); } function aResolvedKeybindingItem({ command, when, isDefault, firstPart, chordPart }: { command?: string; when?: string; isDefault?: boolean; firstPart?: { keyCode: KeyCode; modifiers?: Modifiers }; chordPart?: { keyCode: KeyCode; modifiers?: Modifiers } }): ResolvedKeybindingItem { diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index bf49817490f..cb9b7bf6dbe 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -44,7 +44,7 @@ import { ITextEditorService } from 'vs/workbench/services/textfile/common/textEd import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { isArray, isObject } from 'vs/base/common/types'; import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; const emptyEditableSettingsContent = '{\n}'; @@ -66,7 +66,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic @INotificationService private readonly notificationService: INotificationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, + @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, @ITextModelService private readonly textModelResolverService: ITextModelService, @IKeybindingService keybindingService: IKeybindingService, @IModelService private readonly modelService: IModelService, @@ -93,7 +93,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic private readonly defaultSettingsRawResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/defaultSettings.json' }); get userSettingsResource(): URI { - return this.userDataProfilesService.currentProfile.settingsResource; + return this.userDataProfileService.currentProfile.settingsResource; } get workspaceSettingsResource(): URI | null { @@ -304,7 +304,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic options = { pinned: true, revealIfOpened: true, ...options }; if (textual) { const emptyContents = '// ' + nls.localize('emptyKeybindingsHeader', "Place your key bindings in this file to override the defaults") + '\n[\n]'; - const editableKeybindings = this.userDataProfilesService.currentProfile.keybindingsResource; + const editableKeybindings = this.userDataProfileService.currentProfile.keybindingsResource; const openDefaultKeybindings = !!this.configurationService.getValue('workbench.settings.openDefaultKeybindings'); // Create as needed and open in editor diff --git a/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts b/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts index 954fe75024c..d0781529fb4 100644 --- a/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts +++ b/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts @@ -16,7 +16,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; -import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile, IUserDataProfileService, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceContextService, IWorkspaceIdentifier, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IExtensionManagementServerService, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IHostService } from 'vs/workbench/services/host/browser/host'; @@ -36,6 +36,7 @@ export class UserDataProfileManagementService extends Disposable implements IUse constructor( @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, + @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, @IFileService private readonly fileService: IFileService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, @IWorkbenchExtensionManagementService private readonly extensionManagementService: IWorkbenchExtensionManagementService, @@ -59,25 +60,25 @@ export class UserDataProfileManagementService extends Disposable implements IUse await this.fileService.createFolder(newProfile.location); if (fromExisting) { if (options?.uiState) { - promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.globalStorageHome, newProfile.globalStorageHome)); + promises.push(this.fileService.copy(this.userDataProfileService.currentProfile.globalStorageHome, newProfile.globalStorageHome)); } if (options?.settings) { - promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.settingsResource, newProfile.settingsResource)); + promises.push(this.fileService.copy(this.userDataProfileService.currentProfile.settingsResource, newProfile.settingsResource)); } if (options?.extensions && newProfile.extensionsResource) { promises.push((async () => { - const extensionsProfileResource = this.userDataProfilesService.currentProfile.extensionsResource ?? await this.createDefaultExtensionsProfile(joinPath(this.userDataProfilesService.defaultProfile.location, basename(newProfile.extensionsResource!))); + const extensionsProfileResource = this.userDataProfileService.currentProfile.extensionsResource ?? await this.createDefaultExtensionsProfile(joinPath(this.userDataProfilesService.defaultProfile.location, basename(newProfile.extensionsResource!))); this.fileService.copy(extensionsProfileResource, newProfile.extensionsResource!); })()); } if (options?.keybindings) { - promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.keybindingsResource, newProfile.keybindingsResource)); + promises.push(this.fileService.copy(this.userDataProfileService.currentProfile.keybindingsResource, newProfile.keybindingsResource)); } if (options?.tasks) { - promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.tasksResource, newProfile.tasksResource)); + promises.push(this.fileService.copy(this.userDataProfileService.currentProfile.tasksResource, newProfile.tasksResource)); } if (options?.snippets) { - promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.snippetsHome, newProfile.snippetsHome)); + promises.push(this.fileService.copy(this.userDataProfileService.currentProfile.snippetsHome, newProfile.snippetsHome)); } } else { promises.push(this.fileService.createFolder(newProfile.globalStorageHome)); @@ -97,7 +98,7 @@ export class UserDataProfileManagementService extends Disposable implements IUse if (profile.isDefault) { throw new Error(localize('cannotDeleteDefaultProfile', "Cannot delete the default profile")); } - if (profile.id === this.userDataProfilesService.currentProfile.id) { + if (profile.id === this.userDataProfileService.currentProfile.id) { throw new Error(localize('cannotDeleteCurrentProfile', "Cannot delete the current profile")); } await this.userDataProfilesService.removeProfile(profile); diff --git a/src/vs/workbench/services/userDataProfile/common/settingsProfile.ts b/src/vs/workbench/services/userDataProfile/common/settingsProfile.ts index 8ec179b556f..d725f4feb04 100644 --- a/src/vs/workbench/services/userDataProfile/common/settingsProfile.ts +++ b/src/vs/workbench/services/userDataProfile/common/settingsProfile.ts @@ -8,7 +8,7 @@ import { ConfigurationScope, Extensions, IConfigurationRegistry } from 'vs/platf import { IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { removeComments, updateIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge'; import { IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync'; import { IResourceProfile, ProfileCreationOptions } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; @@ -21,7 +21,7 @@ export class SettingsProfile implements IResourceProfile { constructor( @IFileService private readonly fileService: IFileService, - @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, + @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, @IUserDataSyncUtilService private readonly userDataSyncUtilService: IUserDataSyncUtilService, @ILogService private readonly logService: ILogService, ) { @@ -29,7 +29,7 @@ export class SettingsProfile implements IResourceProfile { async getProfileContent(options?: ProfileCreationOptions): Promise { const ignoredSettings = this.getIgnoredSettings(); - const formattingOptions = await this.userDataSyncUtilService.resolveFormattingOptions(this.userDataProfilesService.currentProfile.settingsResource); + const formattingOptions = await this.userDataSyncUtilService.resolveFormattingOptions(this.userDataProfileService.currentProfile.settingsResource); const localContent = await this.getLocalFileContent(); let settingsProfileContent = updateIgnoredSettings(localContent || '{}', '{}', ignoredSettings, formattingOptions); if (options?.skipComments) { @@ -45,9 +45,9 @@ export class SettingsProfile implements IResourceProfile { const settingsContent: ISettingsContent = JSON.parse(content); this.logService.trace(`Profile: Applying settings...`); const localSettingsContent = await this.getLocalFileContent(); - const formattingOptions = await this.userDataSyncUtilService.resolveFormattingOptions(this.userDataProfilesService.currentProfile.settingsResource); + const formattingOptions = await this.userDataSyncUtilService.resolveFormattingOptions(this.userDataProfileService.currentProfile.settingsResource); const contentToUpdate = updateIgnoredSettings(settingsContent.settings, localSettingsContent || '{}', this.getIgnoredSettings(), formattingOptions); - await this.fileService.writeFile(this.userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString(contentToUpdate)); + await this.fileService.writeFile(this.userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString(contentToUpdate)); this.logService.info(`Profile: Applied settings`); } @@ -59,7 +59,7 @@ export class SettingsProfile implements IResourceProfile { private async getLocalFileContent(): Promise { try { - const content = await this.fileService.readFile(this.userDataProfilesService.currentProfile.settingsResource); + const content = await this.fileService.readFile(this.userDataProfileService.currentProfile.settingsResource); return content.value.toString(); } catch (error) { return null; diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 0609ec29d76..36c73e4aff3 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -160,7 +160,8 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio import { ISocketFactory } from 'vs/platform/remote/common/remoteAgentConnection'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { ILayoutOffsetInfo } from 'vs/platform/layout/browser/layoutService'; -import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService, IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { UserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfileService'; export function createFileEditorInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined, undefined, undefined, undefined, undefined); @@ -281,7 +282,8 @@ export function workbenchInstantiationService( instantiationService.stub(IModelService, disposables.add(instantiationService.createInstance(ModelService))); const fileService = overrides?.fileService ? overrides.fileService(instantiationService) : new TestFileService(); instantiationService.stub(IFileService, fileService); - instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, new NullLogService())); + const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, environmentService, fileService, new NullLogService())); + instantiationService.stub(IUserDataProfileService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService.defaultProfile)); instantiationService.stub(IUriIdentityService, new UriIdentityService(fileService)); instantiationService.stub(IWorkingCopyBackupService, new TestWorkingCopyBackupService()); instantiationService.stub(ITelemetryService, NullTelemetryService); diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index 79d2fefb506..e2125a5e0c0 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -48,9 +48,10 @@ import { IElevatedFileService } from 'vs/workbench/services/files/common/elevate import { IDecorationsService } from 'vs/workbench/services/decorations/common/decorations'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IPartsSplash } from 'vs/platform/theme/common/themeService'; -import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService, IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { FileService } from 'vs/platform/files/common/fileService'; import { joinPath } from 'vs/base/common/resources'; +import { UserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfileService'; const args = parseArgs(process.argv, OPTIONS); @@ -287,7 +288,8 @@ export function workbenchInstantiationService(disposables = new DisposableStore( instantiationService.stub(INativeEnvironmentService, TestEnvironmentService); instantiationService.stub(IWorkbenchEnvironmentService, TestEnvironmentService); instantiationService.stub(INativeWorkbenchEnvironmentService, TestEnvironmentService); - instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, TestEnvironmentService, new FileService(new NullLogService()), new NullLogService())); + const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, TestEnvironmentService, new FileService(new NullLogService()), new NullLogService())); + instantiationService.stub(IUserDataProfileService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService.defaultProfile)); return instantiationService; } From b81bbef2ab3084223c41fa1b998f15d78238c09f Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 17 Jun 2022 09:34:41 +0200 Subject: [PATCH 131/175] move IUserDataProfileService to workbench (#152427) --- .../sharedProcess/sharedProcessMain.ts | 8 ++------ .../storage/browser/storageService.ts | 6 +++--- .../electron-sandbox/storageService.ts | 8 ++++---- .../test/browser/storageService.test.ts | 19 +------------------ .../userDataProfile/common/userDataProfile.ts | 9 --------- src/vs/workbench/browser/web.main.ts | 7 ++++--- .../browser/keybindingsEditorContribution.ts | 2 +- .../browser/preferences.contribution.ts | 2 +- .../common/preferencesContribution.ts | 2 +- .../snippets/browser/configureSnippets.ts | 2 +- .../snippets/browser/snippetsService.ts | 2 +- .../browser/telemetry.contribution.ts | 2 +- .../browser/userDataProfile.ts | 4 ++-- .../common/userDataProfileActions.ts | 4 ++-- .../electron-sandbox/desktop.main.ts | 7 ++++--- .../browser/configurationService.ts | 2 +- .../common/configurationEditingService.ts | 2 +- .../configurationEditingService.test.ts | 5 +++-- .../test/browser/configurationService.test.ts | 5 +++-- .../extensionManagementServerService.ts | 2 +- .../cachedExtensionScanner.ts | 2 +- .../browser/extensionStorageMigration.test.ts | 5 +++-- .../keybinding/browser/keybindingService.ts | 2 +- .../keybinding/common/keybindingEditing.ts | 2 +- .../test/browser/keybindingEditing.test.ts | 5 +++-- .../preferences/browser/preferencesService.ts | 2 +- .../browser/userDataProfileManagement.ts | 4 ++-- .../userDataProfile/common/settingsProfile.ts | 3 +-- .../userDataProfile/common/userDataProfile.ts | 10 ++++++++++ .../common/userDataProfileService.ts | 3 ++- .../test/browser/workbenchTestServices.ts | 5 +++-- .../electron-browser/workbenchTestServices.ts | 5 +++-- 32 files changed, 68 insertions(+), 80 deletions(-) rename src/vs/{platform => workbench/services}/userDataProfile/common/userDataProfileService.ts (87%) diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 5972cfa8985..19d51b95853 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -100,12 +100,11 @@ import { InspectProfilingService as V8InspectProfilingService } from 'vs/platfor import { IV8InspectProfilingService } from 'vs/platform/profiling/common/profiling'; import { IExtensionsScannerService } from 'vs/platform/extensionManagement/common/extensionsScannerService'; import { ExtensionsScannerService } from 'vs/platform/extensionManagement/node/extensionsScannerService'; -import { IUserDataProfileService, IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { ExtensionsProfileScannerService, IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; import { PolicyChannelClient } from 'vs/platform/policy/common/policyIpc'; import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; import { OneDataSystemAppender } from 'vs/platform/telemetry/node/1dsAppender'; -import { UserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfileService'; // import { OneDataSystemAppender } from 'vs/platform/telemetry/node/1dsAppender'; class SharedProcessMain extends Disposable { @@ -236,15 +235,12 @@ class SharedProcessMain extends Disposable { const userDataProfilesService = this._register(new UserDataProfilesService(this.configuration.defaultProfile, environmentService, fileService, logService)); services.set(IUserDataProfilesService, userDataProfilesService); - const userDataProfileService = this._register(new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService.defaultProfile)); - services.set(IUserDataProfileService, userDataProfileService); - // Configuration const configurationService = this._register(new ConfigurationService(userDataProfilesService.defaultProfile.settingsResource, fileService, policyService, logService)); services.set(IConfigurationService, configurationService); // Storage (global access only) - const storageService = new NativeStorageService(undefined, mainProcessService, userDataProfileService, environmentService); + const storageService = new NativeStorageService(undefined, { defaultProfile: userDataProfilesService.defaultProfile, currentProfile: userDataProfilesService.defaultProfile }, mainProcessService, environmentService); services.set(IStorageService, storageService); this._register(toDisposable(() => storageService.flush())); diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts index 6a22eae6de8..3989b25ab47 100644 --- a/src/vs/platform/storage/browser/storageService.ts +++ b/src/vs/platform/storage/browser/storageService.ts @@ -13,7 +13,7 @@ import { assertIsDefined } from 'vs/base/common/types'; import { InMemoryStorageDatabase, isStorageItemsChangeEvent, IStorage, IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest, Storage } from 'vs/base/parts/storage/common/storage'; import { ILogService } from 'vs/platform/log/common/log'; import { AbstractStorageService, IS_NEW_KEY, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { IUserDataProfile, IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IAnyWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export class BrowserStorageService extends AbstractStorageService { @@ -42,12 +42,12 @@ export class BrowserStorageService extends AbstractStorageService { constructor( private readonly payload: IAnyWorkspaceIdentifier, + currentProfile: IUserDataProfile, @ILogService private readonly logService: ILogService, - @IUserDataProfileService userDataProfileService: IUserDataProfileService ) { super({ flushInterval: BrowserStorageService.BROWSER_DEFAULT_FLUSH_INTERVAL }); - this.globalStorageProfile = userDataProfileService.currentProfile; + this.globalStorageProfile = currentProfile; } private getId(scope: StorageScope): string { diff --git a/src/vs/platform/storage/electron-sandbox/storageService.ts b/src/vs/platform/storage/electron-sandbox/storageService.ts index dcf551da3ac..e606e3a1645 100644 --- a/src/vs/platform/storage/electron-sandbox/storageService.ts +++ b/src/vs/platform/storage/electron-sandbox/storageService.ts @@ -11,7 +11,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services'; import { AbstractStorageService, StorageScope, WillSaveStateReason } from 'vs/platform/storage/common/storage'; import { ApplicationStorageDatabaseClient, GlobalStorageDatabaseClient, WorkspaceStorageDatabaseClient } from 'vs/platform/storage/common/storageIpc'; -import { IUserDataProfile, IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IAnyWorkspaceIdentifier, IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export class NativeStorageService extends AbstractStorageService { @@ -29,16 +29,16 @@ export class NativeStorageService extends AbstractStorageService { constructor( workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined, + { defaultProfile, currentProfile }: { defaultProfile: IUserDataProfile; currentProfile: IUserDataProfile }, private readonly mainProcessService: IMainProcessService, - userDataProfileService: IUserDataProfileService, private readonly environmentService: IEnvironmentService ) { super(); - this.applicationStorageProfile = userDataProfileService.defaultProfile; + this.applicationStorageProfile = defaultProfile; this.applicationStorage = this.createApplicationStorage(); - this.globalStorage = this.createGlobalStorage(userDataProfileService.currentProfile); + this.globalStorage = this.createGlobalStorage(currentProfile); this.workspaceStorage = this.createWorkspaceStorage(workspace); } diff --git a/src/vs/platform/storage/test/browser/storageService.test.ts b/src/vs/platform/storage/test/browser/storageService.test.ts index 0d6104ca19d..1e8f38ae028 100644 --- a/src/vs/platform/storage/test/browser/storageService.test.ts +++ b/src/vs/platform/storage/test/browser/storageService.test.ts @@ -18,7 +18,6 @@ import { BrowserStorageService, IndexedDBStorageDatabase } from 'vs/platform/sto import { StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { createSuite } from 'vs/platform/storage/test/common/storageService.test'; import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; -import { UserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfileService'; async function createStorageService(): Promise<[DisposableStore, BrowserStorageService]> { const disposables = new DisposableStore(); @@ -31,20 +30,6 @@ async function createStorageService(): Promise<[DisposableStore, BrowserStorageS const profilesRoot = URI.file('/profiles').with({ scheme: Schemas.inMemory }); - const inMemoryDefaultProfileRoot = joinPath(profilesRoot, 'default'); - const inMemoryDefaultProfile: IUserDataProfile = { - id: 'id', - name: 'inMemory', - isDefault: true, - location: inMemoryDefaultProfileRoot, - globalStorageHome: joinPath(inMemoryDefaultProfileRoot, 'globalStorageHome'), - settingsResource: joinPath(inMemoryDefaultProfileRoot, 'settingsResource'), - keybindingsResource: joinPath(inMemoryDefaultProfileRoot, 'keybindingsResource'), - tasksResource: joinPath(inMemoryDefaultProfileRoot, 'tasksResource'), - snippetsHome: joinPath(inMemoryDefaultProfileRoot, 'snippetsHome'), - extensionsResource: joinPath(inMemoryDefaultProfileRoot, 'extensionsResource') - }; - const inMemoryExtraProfileRoot = joinPath(profilesRoot, 'extra'); const inMemoryExtraProfile: IUserDataProfile = { id: 'id', @@ -59,9 +44,7 @@ async function createStorageService(): Promise<[DisposableStore, BrowserStorageS extensionsResource: joinPath(inMemoryExtraProfileRoot, 'extensionsResource') }; - const userDataProfileService = new UserDataProfileService(inMemoryDefaultProfile, inMemoryExtraProfile); - - const storageService = disposables.add(new BrowserStorageService({ id: 'workspace-storage-test' }, logService, userDataProfileService)); + const storageService = disposables.add(new BrowserStorageService({ id: 'workspace-storage-test' }, inMemoryExtraProfile, logService)); await storageService.initialize(); diff --git a/src/vs/platform/userDataProfile/common/userDataProfile.ts b/src/vs/platform/userDataProfile/common/userDataProfile.ts index 73ec2a89ff7..2991a67cdbb 100644 --- a/src/vs/platform/userDataProfile/common/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/common/userDataProfile.ts @@ -47,15 +47,6 @@ export interface IUserDataProfile { readonly extensionsResource: URI | undefined; } -export const IUserDataProfileService = createDecorator('IUserDataProfileService'); -export interface IUserDataProfileService { - readonly _serviceBrand: undefined; - readonly defaultProfile: IUserDataProfile; - readonly onDidChangeCurrentProfile: Event; - readonly currentProfile: IUserDataProfile; - updateCurrentProfile(currentProfile: IUserDataProfile): void; -} - export function isUserDataProfile(thing: unknown): thing is IUserDataProfile { const candidate = thing as IUserDataProfile | undefined; diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 36bd1e287ab..bd79f5a8820 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -73,12 +73,13 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { DelayedLogChannel } from 'vs/workbench/services/output/common/delayedLogChannel'; import { dirname, joinPath } from 'vs/base/common/resources'; -import { IUserDataProfileService, IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { NullPolicyService } from 'vs/platform/policy/common/policy'; import { IRemoteExplorerService, TunnelSource } from 'vs/workbench/services/remote/common/remoteExplorerService'; import { DisposableTunnel } from 'vs/platform/tunnel/common/tunnel'; import { ILabelService } from 'vs/platform/label/common/label'; -import { UserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfileService'; +import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; export class BrowserMain extends Disposable { @@ -454,7 +455,7 @@ export class BrowserMain extends Disposable { } private async createStorageService(payload: IAnyWorkspaceIdentifier, logService: ILogService, userDataProfileService: IUserDataProfileService): Promise { - const storageService = new BrowserStorageService(payload, logService, userDataProfileService); + const storageService = new BrowserStorageService(payload, userDataProfileService.currentProfile, logService); try { await storageService.initialize(); diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts index 8a6ed721d99..fd43f3b24f5 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts @@ -32,7 +32,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { equals } from 'vs/base/common/arrays'; import { assertIsDefined } from 'vs/base/common/types'; import { isEqual } from 'vs/base/common/resources'; -import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; const NLS_LAUNCH_MESSAGE = nls.localize('defineKeybinding.start', "Define Keybinding"); const NLS_KB_LAYOUT_ERROR_MESSAGE = nls.localize('defineKeybinding.kbLayoutErrorMessage', "You won't be able to produce this key combination under your current keyboard layout."); diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index 2324e9c4b2a..99dc246ef1f 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -43,7 +43,7 @@ import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle import { KeybindingsEditorInput } from 'vs/workbench/services/preferences/browser/keybindingsEditorInput'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; -import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; const SETTINGS_EDITOR_COMMAND_SEARCH = 'settings.action.search'; diff --git a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts index 3825c5e9caa..be64900c807 100644 --- a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts +++ b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts @@ -23,7 +23,7 @@ import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEdit import { RegisteredEditorPriority, IEditorResolverService } from 'vs/workbench/services/editor/common/editorResolverService'; import { ITextEditorService } from 'vs/workbench/services/textfile/common/textEditorService'; import { DEFAULT_SETTINGS_EDITOR_SETTING, FOLDER_SETTINGS_PATH, IPreferencesService, USE_SPLIT_JSON_SETTING } from 'vs/workbench/services/preferences/common/preferences'; -import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; const schemaRegistry = Registry.as(JSONContributionRegistry.Extensions.JSONContribution); diff --git a/src/vs/workbench/contrib/snippets/browser/configureSnippets.ts b/src/vs/workbench/contrib/snippets/browser/configureSnippets.ts index 274dbaf02b5..917e0da44e4 100644 --- a/src/vs/workbench/contrib/snippets/browser/configureSnippets.ts +++ b/src/vs/workbench/contrib/snippets/browser/configureSnippets.ts @@ -18,7 +18,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { isValidBasename } from 'vs/base/common/extpath'; import { joinPath, basename } from 'vs/base/common/resources'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; namespace ISnippetPick { export function is(thing: object | undefined): thing is ISnippetPick { diff --git a/src/vs/workbench/contrib/snippets/browser/snippetsService.ts b/src/vs/workbench/contrib/snippets/browser/snippetsService.ts index a7d9fb27856..5fa9d1d244a 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippetsService.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippetsService.ts @@ -30,7 +30,7 @@ import { isStringArray } from 'vs/base/common/types'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; -import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; namespace snippetExt { diff --git a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts index 3d53f842987..dfcf34f405d 100644 --- a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts +++ b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts @@ -25,7 +25,7 @@ import { getMimeTypes } from 'vs/editor/common/services/languagesAssociations'; import { hash } from 'vs/base/common/hash'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { ViewContainerLocation } from 'vs/workbench/common/views'; -import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; type TelemetryData = { mimeType: string; diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts index cb9215711de..1b94b3fe090 100644 --- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts @@ -9,12 +9,12 @@ import { localize } from 'vs/nls'; import { Action2, ISubmenuItem, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IsDevelopmentContext } from 'vs/platform/contextkey/common/contextkeys'; -import { IUserDataProfile, IUserDataProfileService, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { WorkbenchStateContext } from 'vs/workbench/common/contextkeys'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar'; -import { IUserDataProfileManagementService, ManageProfilesSubMenu, PROFILES_CATEGORY, PROFILES_TTILE } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; +import { IUserDataProfileManagementService, IUserDataProfileService, ManageProfilesSubMenu, PROFILES_CATEGORY, PROFILES_TTILE } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; const CONTEXT_CURRENT_PROFILE = new RawContextKey('currentUserDataProfile', ''); diff --git a/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts b/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts index 97d88b2b3df..82cee39c1e6 100644 --- a/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts +++ b/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts @@ -14,9 +14,9 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { INotificationService } from 'vs/platform/notification/common/notification'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { asJson, asText, IRequestService } from 'vs/platform/request/common/request'; -import { IUserDataProfileTemplate, isUserDataProfileTemplate, IUserDataProfileManagementService, IUserDataProfileWorkbenchService, PROFILES_CATEGORY, PROFILE_EXTENSION, PROFILE_FILTER, ManageProfilesSubMenu } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; +import { IUserDataProfileTemplate, isUserDataProfileTemplate, IUserDataProfileManagementService, IUserDataProfileWorkbenchService, PROFILES_CATEGORY, PROFILE_EXTENSION, PROFILE_FILTER, ManageProfilesSubMenu, IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IUserDataProfile, IUserDataProfileService, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IsDevelopmentContext } from 'vs/platform/contextkey/common/contextkeys'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { WorkbenchStateContext } from 'vs/workbench/common/contextkeys'; diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts index 7bcef13cecc..a30fad8a969 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -50,11 +50,12 @@ import { isCI, isMacintosh } from 'vs/base/common/platform'; import { Schemas } from 'vs/base/common/network'; import { DiskFileSystemProvider } from 'vs/workbench/services/files/electron-sandbox/diskFileSystemProvider'; import { FileUserDataProvider } from 'vs/platform/userData/common/fileUserDataProvider'; -import { IUserDataProfileService, IUserDataProfilesService, reviveProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfilesService, reviveProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { UserDataProfilesNativeService } from 'vs/platform/userDataProfile/electron-sandbox/userDataProfile'; import { PolicyChannelClient } from 'vs/platform/policy/common/policyIpc'; import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; -import { UserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfileService'; +import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; export class DesktopMain extends Disposable { @@ -364,7 +365,7 @@ export class DesktopMain extends Disposable { } private async createStorageService(payload: IAnyWorkspaceIdentifier, environmentService: INativeWorkbenchEnvironmentService, userDataProfileService: IUserDataProfileService, mainProcessService: IMainProcessService): Promise { - const storageService = new NativeStorageService(payload, mainProcessService, userDataProfileService, environmentService); + const storageService = new NativeStorageService(payload, userDataProfileService, mainProcessService, environmentService); try { await storageService.initialize(); diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 81a8d2b63a1..24d461f8e3c 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -40,7 +40,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService'; import { isUndefined } from 'vs/base/common/types'; import { localize } from 'vs/nls'; -import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; class Workspace extends BaseWorkspace { diff --git a/src/vs/workbench/services/configuration/common/configurationEditingService.ts b/src/vs/workbench/services/configuration/common/configurationEditingService.ts index 0ec0b7b1d19..74e507c17b1 100644 --- a/src/vs/workbench/services/configuration/common/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditingService.ts @@ -28,7 +28,7 @@ import { IReference } from 'vs/base/common/lifecycle'; import { Range } from 'vs/editor/common/core/range'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Selection } from 'vs/editor/common/core/selection'; -import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; export const enum ConfigurationEditingErrorCode { diff --git a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts index d2026aacd49..61c7a7de517 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts @@ -39,12 +39,13 @@ import { joinPath } from 'vs/base/common/resources'; import { VSBuffer } from 'vs/base/common/buffer'; import { RemoteAgentService } from 'vs/workbench/services/remote/browser/remoteAgentService'; import { getSingleFolderWorkspaceIdentifier } from 'vs/workbench/services/workspaces/browser/workspaces'; -import { DefaultOptions, IUserDataProfileService, toUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { DefaultOptions, toUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { hash } from 'vs/base/common/hash'; import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; -import { UserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfileService'; +import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; const ROOT = URI.file('tests').with({ scheme: 'vscode-tests' }); diff --git a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts index 18c28ba27a5..a7c7d062087 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts @@ -44,11 +44,12 @@ import { RemoteAgentService } from 'vs/workbench/services/remote/browser/remoteA import { RemoteAuthorityResolverService } from 'vs/platform/remote/browser/remoteAuthorityResolverService'; import { hash } from 'vs/base/common/hash'; import { TestProductService } from 'vs/workbench/test/common/workbenchTestServices'; -import { DefaultOptions, IUserDataProfileService, toUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { DefaultOptions, toUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { NullPolicyService } from 'vs/platform/policy/common/policy'; import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; -import { UserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfileService'; +import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; function convertToWorkspacePayload(folder: URI): ISingleFolderWorkspaceIdentifier { return { diff --git a/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService.ts b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService.ts index 292e2bfe2d2..e8fe314d41b 100644 --- a/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService.ts @@ -15,7 +15,7 @@ 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 { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; -import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { URI } from 'vs/base/common/uri'; export class ExtensionManagementServerService implements IExtensionManagementServerService { diff --git a/src/vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner.ts index c6602496838..dd9be154068 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner.ts @@ -15,7 +15,7 @@ import { localize } from 'vs/nls'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { timeout } from 'vs/base/common/async'; -import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; export class CachedExtensionScanner { diff --git a/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts b/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts index 25deefb3839..5b9c83b5b8e 100644 --- a/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts +++ b/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts @@ -19,8 +19,9 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { migrateExtensionStorage } from 'vs/workbench/services/extensions/common/extensionStorageMigration'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { IUserDataProfileService, IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; -import { UserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfileService'; +import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; suite('ExtensionStorageMigration', () => { diff --git a/src/vs/workbench/services/keybinding/browser/keybindingService.ts b/src/vs/workbench/services/keybinding/browser/keybindingService.ts index 67ef5b5ec6a..fe43ddc55ed 100644 --- a/src/vs/workbench/services/keybinding/browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/browser/keybindingService.ts @@ -51,7 +51,7 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { dirname } from 'vs/base/common/resources'; import { getAllUnboundCommands } from 'vs/workbench/services/keybinding/browser/unboundCommands'; import { UserSettingsLabelProvider } from 'vs/base/common/keybindingLabels'; -import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; interface ContributedKeyBinding { command: string; diff --git a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts index 732b38857de..a50ba251695 100644 --- a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts +++ b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts @@ -25,7 +25,7 @@ import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybindin import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; export const IKeybindingEditingService = createDecorator('keybindingEditingService'); diff --git a/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts b/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts index 1c2906db471..eee64e018fe 100644 --- a/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts +++ b/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts @@ -28,8 +28,9 @@ import { joinPath } from 'vs/base/common/resources'; import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { VSBuffer } from 'vs/base/common/buffer'; -import { DefaultOptions, IUserDataProfileService, toUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; -import { UserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfileService'; +import { DefaultOptions, toUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; interface Modifiers { metaKey?: boolean; diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index cb9b7bf6dbe..fa8078055ce 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -44,7 +44,7 @@ import { ITextEditorService } from 'vs/workbench/services/textfile/common/textEd import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { isArray, isObject } from 'vs/base/common/types'; import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController'; -import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; const emptyEditableSettingsContent = '{\n}'; diff --git a/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts b/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts index d0781529fb4..15085949c96 100644 --- a/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts +++ b/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts @@ -16,11 +16,11 @@ import { IFileService } from 'vs/platform/files/common/files'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; -import { IUserDataProfile, IUserDataProfileService, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceContextService, IWorkspaceIdentifier, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IExtensionManagementServerService, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { CreationOptions, IUserDataProfileManagementService, IUserDataProfileTemplate, PROFILES_CATEGORY } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; +import { CreationOptions, IUserDataProfileManagementService, IUserDataProfileService, IUserDataProfileTemplate, PROFILES_CATEGORY } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; const DefaultOptions: CreationOptions = { settings: true, diff --git a/src/vs/workbench/services/userDataProfile/common/settingsProfile.ts b/src/vs/workbench/services/userDataProfile/common/settingsProfile.ts index d725f4feb04..6cc9a6d284f 100644 --- a/src/vs/workbench/services/userDataProfile/common/settingsProfile.ts +++ b/src/vs/workbench/services/userDataProfile/common/settingsProfile.ts @@ -8,10 +8,9 @@ import { ConfigurationScope, Extensions, IConfigurationRegistry } from 'vs/platf import { IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService, IResourceProfile, ProfileCreationOptions } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { removeComments, updateIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge'; import { IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync'; -import { IResourceProfile, ProfileCreationOptions } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; interface ISettingsContent { settings: string; diff --git a/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts b/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts index 7a49cb852de..a033e55d951 100644 --- a/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts +++ b/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { isUndefined } from 'vs/base/common/types'; +import { Event } from 'vs/base/common/event'; import { localize } from 'vs/nls'; import { MenuId } from 'vs/platform/actions/common/actions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -18,6 +19,15 @@ export type CreationOptions = { uiState?: boolean; }; +export const IUserDataProfileService = createDecorator('IUserDataProfileService'); +export interface IUserDataProfileService { + readonly _serviceBrand: undefined; + readonly defaultProfile: IUserDataProfile; + readonly onDidChangeCurrentProfile: Event; + readonly currentProfile: IUserDataProfile; + updateCurrentProfile(currentProfile: IUserDataProfile): void; +} + export const IUserDataProfileManagementService = createDecorator('IUserDataProfileManagementService'); export interface IUserDataProfileManagementService { readonly _serviceBrand: undefined; diff --git a/src/vs/platform/userDataProfile/common/userDataProfileService.ts b/src/vs/workbench/services/userDataProfile/common/userDataProfileService.ts similarity index 87% rename from src/vs/platform/userDataProfile/common/userDataProfileService.ts rename to src/vs/workbench/services/userDataProfile/common/userDataProfileService.ts index 615e34944ef..756d7acc6cd 100644 --- a/src/vs/platform/userDataProfile/common/userDataProfileService.ts +++ b/src/vs/workbench/services/userDataProfile/common/userDataProfileService.ts @@ -5,7 +5,8 @@ import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IUserDataProfile, IUserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; export class UserDataProfileService extends Disposable implements IUserDataProfileService { diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 36c73e4aff3..d4caf868681 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -160,8 +160,9 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio import { ISocketFactory } from 'vs/platform/remote/common/remoteAgentConnection'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { ILayoutOffsetInfo } from 'vs/platform/layout/browser/layoutService'; -import { IUserDataProfileService, IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; -import { UserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfileService'; +import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; export function createFileEditorInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined, undefined, undefined, undefined, undefined); diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index e2125a5e0c0..85cb72d44de 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -48,10 +48,11 @@ import { IElevatedFileService } from 'vs/workbench/services/files/common/elevate import { IDecorationsService } from 'vs/workbench/services/decorations/common/decorations'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IPartsSplash } from 'vs/platform/theme/common/themeService'; -import { IUserDataProfileService, IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { FileService } from 'vs/platform/files/common/fileService'; import { joinPath } from 'vs/base/common/resources'; -import { UserDataProfileService } from 'vs/platform/userDataProfile/common/userDataProfileService'; +import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; const args = parseArgs(process.argv, OPTIONS); From 54003f85adfd4b48dbcaefdce355ba15b6ee2e20 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 17 Jun 2022 09:40:36 +0200 Subject: [PATCH 132/175] debt - cleanup config migration (#152425) --- .../browser/workbench.contribution.ts | 8 +- src/vs/workbench/common/configuration.ts | 91 +++++++++++++++++ .../common/configurationMigration.ts | 99 ------------------- .../browser/editorSettingsMigration.ts | 2 +- .../search/browser/search.contribution.ts | 2 +- 5 files changed, 99 insertions(+), 103 deletions(-) delete mode 100644 src/vs/workbench/common/configurationMigration.ts diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index e1a960c81de..017f41f481a 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -7,15 +7,19 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { localize } from 'vs/nls'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { isMacintosh, isWindows, isLinux, isWeb, isNative } from 'vs/base/common/platform'; -import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; +import { ConfigurationMigrationWorkbenchContribution, workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; import { isStandalone } from 'vs/base/browser/browser'; -import 'vs/workbench/common/configurationMigration'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; const registry = Registry.as(ConfigurationExtensions.Configuration); // Configuration (function registerConfiguration(): void { + // Migration support + Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ConfigurationMigrationWorkbenchContribution, LifecyclePhase.Eventually); + // Workbench registry.registerConfiguration({ ...workbenchConfigurationNodeBase, diff --git a/src/vs/workbench/common/configuration.ts b/src/vs/workbench/common/configuration.ts index 8405ae63a5f..d25d5728cef 100644 --- a/src/vs/workbench/common/configuration.ts +++ b/src/vs/workbench/common/configuration.ts @@ -5,6 +5,12 @@ import { localize } from 'vs/nls'; import { IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { ConfigurationTarget, IConfigurationOverrides, IConfigurationService, IConfigurationValue } from 'vs/platform/configuration/common/configuration'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { Emitter } from 'vs/base/common/event'; export const workbenchConfigurationNodeBase = Object.freeze({ 'id': 'workbench', @@ -12,3 +18,88 @@ export const workbenchConfigurationNodeBase = Object.freeze( 'title': localize('workbenchConfigurationTitle', "Workbench"), 'type': 'object', }); + +export const Extensions = { + ConfigurationMigration: 'base.contributions.configuration.migration' +}; + +export type ConfigurationValue = { value: any | undefined /* Remove */ }; +export type ConfigurationKeyValuePairs = [string, ConfigurationValue][]; +export type ConfigurationMigrationFn = (value: any, valueAccessor: (key: string) => any) => ConfigurationValue | ConfigurationKeyValuePairs | Promise; +export type ConfigurationMigration = { key: string; migrateFn: ConfigurationMigrationFn }; + +export interface IConfigurationMigrationRegistry { + registerConfigurationMigrations(configurationMigrations: ConfigurationMigration[]): void; +} + +class ConfigurationMigrationRegistry implements IConfigurationMigrationRegistry { + + readonly migrations: ConfigurationMigration[] = []; + + private readonly _onDidRegisterConfigurationMigrations = new Emitter(); + readonly onDidRegisterConfigurationMigration = this._onDidRegisterConfigurationMigrations.event; + + registerConfigurationMigrations(configurationMigrations: ConfigurationMigration[]): void { + this.migrations.push(...configurationMigrations); + } + +} + +const configurationMigrationRegistry = new ConfigurationMigrationRegistry(); +Registry.add(Extensions.ConfigurationMigration, configurationMigrationRegistry); + +export class ConfigurationMigrationWorkbenchContribution extends Disposable implements IWorkbenchContribution { + + constructor( + @IConfigurationService private readonly configurationService: IConfigurationService, + @IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService, + ) { + super(); + this._register(this.workspaceService.onDidChangeWorkspaceFolders(async (e) => { + for (const folder of e.added) { + await this.migrateConfigurationsForFolder(folder, configurationMigrationRegistry.migrations); + } + })); + this.migrateConfigurations(configurationMigrationRegistry.migrations); + this._register(configurationMigrationRegistry.onDidRegisterConfigurationMigration(migration => this.migrateConfigurations(migration))); + } + + private async migrateConfigurations(migrations: ConfigurationMigration[]): Promise { + await this.migrateConfigurationsForFolder(undefined, migrations); + for (const folder of this.workspaceService.getWorkspace().folders) { + await this.migrateConfigurationsForFolder(folder, migrations); + } + } + + private async migrateConfigurationsForFolder(folder: IWorkspaceFolder | undefined, migrations: ConfigurationMigration[]): Promise { + await Promise.all(migrations.map(migration => this.migrateConfigurationsForFolderAndOverride(migration, { resource: folder?.uri }))); + } + + private async migrateConfigurationsForFolderAndOverride(migration: ConfigurationMigration, overrides: IConfigurationOverrides): Promise { + const data = this.configurationService.inspect(migration.key, overrides); + + await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'userValue', ConfigurationTarget.USER); + await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'userLocalValue', ConfigurationTarget.USER_LOCAL); + await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'userRemoteValue', ConfigurationTarget.USER_REMOTE); + await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'workspaceFolderValue', ConfigurationTarget.WORKSPACE_FOLDER); + await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'workspaceValue', ConfigurationTarget.WORKSPACE); + + if (typeof overrides.overrideIdentifier === 'undefined' && typeof data.overrideIdentifiers !== 'undefined') { + for (const overrideIdentifier of data.overrideIdentifiers) { + await this.migrateConfigurationsForFolderAndOverride(migration, { resource: overrides.resource, overrideIdentifier }); + } + } + } + + private async migrateConfigurationForFolderOverrideAndTarget(migration: ConfigurationMigration, overrides: IConfigurationOverrides, data: IConfigurationValue, dataKey: keyof IConfigurationValue, target: ConfigurationTarget): Promise { + const value = data[dataKey]; + if (typeof value === 'undefined') { + return; + } + + const valueAccessor = (key: string) => this.configurationService.inspect(key, overrides)[dataKey]; + const result = await migration.migrateFn(value, valueAccessor); + const keyValuePairs: ConfigurationKeyValuePairs = Array.isArray(result) ? result : [[migration.key, result]]; + await Promise.allSettled(keyValuePairs.map(async ([key, value]) => this.configurationService.updateValue(key, value.value, overrides, target))); + } +} diff --git a/src/vs/workbench/common/configurationMigration.ts b/src/vs/workbench/common/configurationMigration.ts deleted file mode 100644 index 44bcd77a4ad..00000000000 --- a/src/vs/workbench/common/configurationMigration.ts +++ /dev/null @@ -1,99 +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 { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; -import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { ConfigurationTarget, IConfigurationOverrides, IConfigurationService, IConfigurationValue } from 'vs/platform/configuration/common/configuration'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { Emitter } from 'vs/base/common/event'; - -export const Extensions = { - ConfigurationMigration: 'base.contributions.configuration.migration' -}; - -export type ConfigurationValue = { value: any | undefined /* Remove */ }; -export type ConfigurationKeyValuePairs = [string, ConfigurationValue][]; -export type ConfigurationMigrationFn = (value: any, valueAccessor: (key: string) => any) => ConfigurationValue | ConfigurationKeyValuePairs | Promise; -export type ConfigurationMigration = { key: string; migrateFn: ConfigurationMigrationFn }; - -export interface IConfigurationMigrationRegistry { - registerConfigurationMigrations(configurationMigrations: ConfigurationMigration[]): void; -} - -class ConfigurationMigrationRegistry implements IConfigurationMigrationRegistry { - - readonly migrations: ConfigurationMigration[] = []; - - private readonly _onDidRegisterConfigurationMigrations = new Emitter(); - readonly onDidRegisterConfigurationMigration = this._onDidRegisterConfigurationMigrations.event; - - registerConfigurationMigrations(configurationMigrations: ConfigurationMigration[]): void { - this.migrations.push(...configurationMigrations); - } - -} - -const configurationMigrationRegistry = new ConfigurationMigrationRegistry(); -Registry.add(Extensions.ConfigurationMigration, configurationMigrationRegistry); - -class ConfigurationMigrationWorkbenchContribution extends Disposable implements IWorkbenchContribution { - - constructor( - @IConfigurationService private readonly configurationService: IConfigurationService, - @IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService, - ) { - super(); - this._register(this.workspaceService.onDidChangeWorkspaceFolders(async (e) => { - for (const folder of e.added) { - await this.migrateConfigurationsForFolder(folder, configurationMigrationRegistry.migrations); - } - })); - this.migrateConfigurations(configurationMigrationRegistry.migrations); - this._register(configurationMigrationRegistry.onDidRegisterConfigurationMigration(migration => this.migrateConfigurations(migration))); - } - - private async migrateConfigurations(migrations: ConfigurationMigration[]): Promise { - await this.migrateConfigurationsForFolder(undefined, migrations); - for (const folder of this.workspaceService.getWorkspace().folders) { - await this.migrateConfigurationsForFolder(folder, migrations); - } - } - - private async migrateConfigurationsForFolder(folder: IWorkspaceFolder | undefined, migrations: ConfigurationMigration[]): Promise { - await Promise.all(migrations.map(migration => this.migrateConfigurationsForFolderAndOverride(migration, { resource: folder?.uri }))); - } - - private async migrateConfigurationsForFolderAndOverride(migration: ConfigurationMigration, overrides: IConfigurationOverrides): Promise { - const data = this.configurationService.inspect(migration.key, overrides); - - await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'userValue', ConfigurationTarget.USER); - await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'userLocalValue', ConfigurationTarget.USER_LOCAL); - await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'userRemoteValue', ConfigurationTarget.USER_REMOTE); - await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'workspaceFolderValue', ConfigurationTarget.WORKSPACE_FOLDER); - await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'workspaceValue', ConfigurationTarget.WORKSPACE); - - if (typeof overrides.overrideIdentifier === 'undefined' && typeof data.overrideIdentifiers !== 'undefined') { - for (const overrideIdentifier of data.overrideIdentifiers) { - await this.migrateConfigurationsForFolderAndOverride(migration, { resource: overrides.resource, overrideIdentifier }); - } - } - } - - private async migrateConfigurationForFolderOverrideAndTarget(migration: ConfigurationMigration, overrides: IConfigurationOverrides, data: IConfigurationValue, dataKey: keyof IConfigurationValue, target: ConfigurationTarget): Promise { - const value = data[dataKey]; - if (typeof value === 'undefined') { - return; - } - - const valueAccessor = (key: string) => this.configurationService.inspect(key, overrides)[dataKey]; - const result = await migration.migrateFn(value, valueAccessor); - const keyValuePairs: ConfigurationKeyValuePairs = Array.isArray(result) ? result : [[migration.key, result]]; - await Promise.allSettled(keyValuePairs.map(async ([key, value]) => this.configurationService.updateValue(key, value.value, overrides, target))); - } -} - -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ConfigurationMigrationWorkbenchContribution, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/codeEditor/browser/editorSettingsMigration.ts b/src/vs/workbench/contrib/codeEditor/browser/editorSettingsMigration.ts index 6a08839d154..6803bea8f1a 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/editorSettingsMigration.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/editorSettingsMigration.ts @@ -5,7 +5,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { EditorSettingMigration, ISettingsWriter } from 'vs/editor/browser/config/migrateOptions'; -import { ConfigurationKeyValuePairs, Extensions, IConfigurationMigrationRegistry } from 'vs/workbench/common/configurationMigration'; +import { ConfigurationKeyValuePairs, Extensions, IConfigurationMigrationRegistry } from 'vs/workbench/common/configuration'; Registry.as(Extensions.ConfigurationMigration) .registerConfigurationMigrations(EditorSettingMigration.items.map(item => ({ diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index b19a449124a..77782e09059 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -55,7 +55,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { ISearchConfiguration, SearchSortOrder, SEARCH_EXCLUDE_CONFIG, VIEWLET_ID, VIEW_ID } from 'vs/workbench/services/search/common/search'; -import { Extensions, IConfigurationMigrationRegistry } from 'vs/workbench/common/configurationMigration'; +import { Extensions, IConfigurationMigrationRegistry } from 'vs/workbench/common/configuration'; registerSingleton(ISearchWorkbenchService, SearchWorkbenchService, true); registerSingleton(ISearchHistoryService, SearchHistoryService, true); From 623f55f437ab31fa56e13acafa9b9e7d4b50c4bf Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 17 Jun 2022 01:25:52 -0700 Subject: [PATCH 133/175] Refactor markdown language features (#152402) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (sorry for the size of this PR) This change cleans up the markdown language features by making the following changes: - Use `registerXSupport` public functions to register these - Expose the slugifier the `MarkdownEngine` uses. You never want to use a different one if you already have a markdown engine - Sort of clean up names. I'd introduced a bunch of confusing names while iterating in this space. What I'm working towards: - `Computer` — Stateless thing that computer data - `Provider` — Potentially stateful thing that provides data (which may be cached) - `VsCodeProvider` — The actual implementation of the various vscode language features (which should only be used by VS Code and in tests, not shared with other features) - Introduce `MdLinkProvider` to avoid recomputing links for a given document. Also use this to hide more internals of link computation --- .../src/extension.ts | 66 ++++++------- .../src/languageFeatures/copyPaste.ts | 2 +- .../languageFeatures/definitionProvider.ts | 20 ++-- .../src/languageFeatures/diagnostics.ts | 29 +++--- .../languageFeatures/documentLinkProvider.ts | 69 ++++++++++---- .../documentSymbolProvider.ts | 7 ++ .../src/languageFeatures/dropIntoEditor.ts | 2 +- .../src/languageFeatures/fileReferences.ts | 11 ++- .../src/languageFeatures/foldingProvider.ts | 7 ++ .../src/languageFeatures/pathCompletions.ts | 29 +++--- .../src/languageFeatures/references.ts | 36 +++---- .../src/languageFeatures/rename.ts | 17 +++- .../src/languageFeatures/smartSelect.ts | 7 ++ .../src/languageFeatures/workspaceCache.ts | 93 +++++++++++++++++-- .../workspaceSymbolProvider.ts | 13 ++- .../src/markdownEngine.ts | 6 +- .../src/test/definitionProvider.test.ts | 11 +-- .../src/test/diagnostic.test.ts | 21 ++--- .../src/test/documentLinkProvider.test.ts | 16 ++-- .../src/test/documentSymbolProvider.test.ts | 8 +- .../src/test/fileReferences.test.ts | 7 +- .../src/test/pathCompletion.test.ts | 11 ++- .../src/test/references.test.ts | 7 +- .../src/test/rename.test.ts | 19 ++-- 24 files changed, 333 insertions(+), 181 deletions(-) diff --git a/extensions/markdown-language-features/src/extension.ts b/extensions/markdown-language-features/src/extension.ts index 97fd770e64f..be3a7858fe8 100644 --- a/extensions/markdown-language-features/src/extension.ts +++ b/extensions/markdown-language-features/src/extension.ts @@ -6,19 +6,19 @@ import * as vscode from 'vscode'; import { CommandManager } from './commandManager'; import * as commands from './commands/index'; -import { registerPasteProvider } from './languageFeatures/copyPaste'; -import { MdDefinitionProvider } from './languageFeatures/definitionProvider'; -import { register as registerDiagnostics } from './languageFeatures/diagnostics'; -import { MdLinkComputer, registerDocumentLinkProvider } from './languageFeatures/documentLinkProvider'; -import { MdDocumentSymbolProvider } from './languageFeatures/documentSymbolProvider'; -import { registerDropIntoEditor } from './languageFeatures/dropIntoEditor'; -import { registerFindFileReferences } from './languageFeatures/fileReferences'; -import { MdFoldingProvider } from './languageFeatures/foldingProvider'; -import { MdPathCompletionProvider } from './languageFeatures/pathCompletions'; -import { MdReferencesComputer, registerReferencesProvider } from './languageFeatures/references'; -import { MdRenameProvider } from './languageFeatures/rename'; -import { MdSmartSelect } from './languageFeatures/smartSelect'; -import { MdWorkspaceSymbolProvider } from './languageFeatures/workspaceSymbolProvider'; +import { registerPasteSupport } from './languageFeatures/copyPaste'; +import { registerDefinitionSupport } from './languageFeatures/definitionProvider'; +import { registerDiagnosticSupport } from './languageFeatures/diagnostics'; +import { MdLinkProvider, registerDocumentLinkSupport } from './languageFeatures/documentLinkProvider'; +import { MdDocumentSymbolProvider, registerDocumentSymbolSupport } from './languageFeatures/documentSymbolProvider'; +import { registerDropIntoEditorSupport } from './languageFeatures/dropIntoEditor'; +import { registerFindFileReferenceSupport } from './languageFeatures/fileReferences'; +import { registerFoldingSupport } from './languageFeatures/foldingProvider'; +import { registerPathCompletionSupport } from './languageFeatures/pathCompletions'; +import { MdReferencesProvider, registerReferencesSupport } from './languageFeatures/references'; +import { registerRenameSupport } from './languageFeatures/rename'; +import { registerSmartSelectSupport } from './languageFeatures/smartSelect'; +import { registerWorkspaceSymbolSupport } from './languageFeatures/workspaceSymbolProvider'; import { Logger } from './logger'; import { MarkdownEngine } from './markdownEngine'; import { getMarkdownExtensionContributions } from './markdownExtensions'; @@ -43,11 +43,10 @@ export function activate(context: vscode.ExtensionContext) { const commandManager = new CommandManager(); const contentProvider = new MarkdownContentProvider(engine, context, cspArbiter, contributions, logger); - const symbolProvider = new MdDocumentSymbolProvider(engine); const previewManager = new MarkdownPreviewManager(contentProvider, logger, contributions, engine); context.subscriptions.push(previewManager); - context.subscriptions.push(registerMarkdownLanguageFeatures(commandManager, symbolProvider, engine)); + context.subscriptions.push(registerMarkdownLanguageFeatures(commandManager, engine)); context.subscriptions.push(registerMarkdownCommands(commandManager, previewManager, telemetryReporter, cspArbiter, engine)); context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(() => { @@ -58,30 +57,35 @@ export function activate(context: vscode.ExtensionContext) { function registerMarkdownLanguageFeatures( commandManager: CommandManager, - symbolProvider: MdDocumentSymbolProvider, engine: MarkdownEngine ): vscode.Disposable { const selector: vscode.DocumentSelector = { language: 'markdown', scheme: '*' }; - const linkComputer = new MdLinkComputer(engine); const workspaceContents = new VsCodeMdWorkspaceContents(); - const referencesComputer = new MdReferencesComputer(linkComputer, workspaceContents, engine, githubSlugifier); + const linkProvider = new MdLinkProvider(engine, workspaceContents); + const referencesProvider = new MdReferencesProvider(engine, workspaceContents); + const symbolProvider = new MdDocumentSymbolProvider(engine); + return vscode.Disposable.from( workspaceContents, - vscode.languages.registerDocumentSymbolProvider(selector, symbolProvider), - vscode.languages.registerFoldingRangeProvider(selector, new MdFoldingProvider(engine)), - vscode.languages.registerSelectionRangeProvider(selector, new MdSmartSelect(engine)), - vscode.languages.registerWorkspaceSymbolProvider(new MdWorkspaceSymbolProvider(symbolProvider, workspaceContents)), - vscode.languages.registerRenameProvider(selector, new MdRenameProvider(referencesComputer, workspaceContents, githubSlugifier)), - vscode.languages.registerDefinitionProvider(selector, new MdDefinitionProvider(referencesComputer)), - MdPathCompletionProvider.register(selector, engine, linkComputer), - registerDocumentLinkProvider(selector, linkComputer), - registerDiagnostics(selector, engine, workspaceContents, linkComputer, commandManager, referencesComputer), - registerDropIntoEditor(selector), - registerReferencesProvider(selector, referencesComputer), - registerPasteProvider(selector), - registerFindFileReferences(commandManager, referencesComputer), + linkProvider, + referencesProvider, + + // Language features + registerDefinitionSupport(selector, referencesProvider), + registerDiagnosticSupport(selector, engine, workspaceContents, linkProvider, commandManager, referencesProvider), + registerDocumentLinkSupport(selector, linkProvider), + registerDocumentSymbolSupport(selector, engine), + registerDropIntoEditorSupport(selector), + registerFindFileReferenceSupport(commandManager, referencesProvider), + registerFoldingSupport(selector, engine), + registerPasteSupport(selector), + registerPathCompletionSupport(selector, engine, linkProvider), + registerReferencesSupport(selector, referencesProvider), + registerRenameSupport(selector, workspaceContents, referencesProvider, engine.slugifier), + registerSmartSelectSupport(selector, engine), + registerWorkspaceSymbolSupport(workspaceContents, symbolProvider), ); } diff --git a/extensions/markdown-language-features/src/languageFeatures/copyPaste.ts b/extensions/markdown-language-features/src/languageFeatures/copyPaste.ts index 242bc4a0f52..fe6c119b012 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyPaste.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyPaste.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; import { tryGetUriListSnippet } from './dropIntoEditor'; -export function registerPasteProvider(selector: vscode.DocumentSelector) { +export function registerPasteSupport(selector: vscode.DocumentSelector) { return vscode.languages.registerDocumentPasteEditProvider(selector, new class implements vscode.DocumentPasteEditProvider { async provideDocumentPasteEdits( diff --git a/extensions/markdown-language-features/src/languageFeatures/definitionProvider.ts b/extensions/markdown-language-features/src/languageFeatures/definitionProvider.ts index dbaeacf1a46..06d9db8a3bc 100644 --- a/extensions/markdown-language-features/src/languageFeatures/definitionProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/definitionProvider.ts @@ -3,21 +3,25 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { Disposable } from '../util/dispose'; import { SkinnyTextDocument } from '../workspaceContents'; -import { MdReferencesComputer } from './references'; +import { MdReferencesProvider } from './references'; -export class MdDefinitionProvider extends Disposable implements vscode.DefinitionProvider { +export class MdDefinitionProvider implements vscode.DefinitionProvider { constructor( - private readonly referencesComputer: MdReferencesComputer - ) { - super(); - } + private readonly referencesProvider: MdReferencesProvider + ) { } async provideDefinition(document: SkinnyTextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { - const allRefs = await this.referencesComputer.getReferencesAtPosition(document, position, token); + const allRefs = await this.referencesProvider.getReferencesAtPosition(document, position, token); return allRefs.find(ref => ref.kind === 'link' && ref.isDefinition)?.location; } } + +export function registerDefinitionSupport( + selector: vscode.DocumentSelector, + referencesProvider: MdReferencesProvider, +): vscode.Disposable { + return vscode.languages.registerDefinitionProvider(selector, new MdDefinitionProvider(referencesProvider)); +} diff --git a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts index 686bfe7c241..8a1dbe1c619 100644 --- a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts @@ -17,8 +17,8 @@ import { Limiter } from '../util/limiter'; import { ResourceMap } from '../util/resourceMap'; import { MdTableOfContentsWatcher } from '../test/tableOfContentsWatcher'; import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; -import { InternalHref, LinkDefinitionSet, MdLink, MdLinkComputer, MdLinkSource } from './documentLinkProvider'; -import { MdReferencesComputer, tryFindMdDocumentForLink } from './references'; +import { InternalHref, MdLink, MdLinkSource, MdLinkProvider, LinkDefinitionSet } from './documentLinkProvider'; +import { MdReferencesProvider, tryFindMdDocumentForLink } from './references'; const localize = nls.loadMessageBundle(); @@ -305,7 +305,7 @@ export class DiagnosticManager extends Disposable { private readonly computer: DiagnosticComputer, private readonly configuration: DiagnosticConfiguration, private readonly reporter: DiagnosticReporter, - private readonly referencesComputer: MdReferencesComputer, + private readonly referencesProvider: MdReferencesProvider, delay = 300, ) { super(); @@ -344,7 +344,7 @@ export class DiagnosticManager extends Disposable { this._register(this.tableOfContentsWatcher.onTocChanged(async e => { // When the toc of a document changes, revalidate every file that linked to it too const triggered = new ResourceMap(); - for (const ref of await this.referencesComputer.getAllReferencesToFile(e.uri, noopToken)) { + for (const ref of await this.referencesProvider.getAllReferencesToFile(e.uri, noopToken)) { const file = ref.location.uri; if (!triggered.has(file)) { this.triggerDiagnostics(file); @@ -450,11 +450,11 @@ export class DiagnosticComputer { constructor( private readonly engine: MarkdownEngine, private readonly workspaceContents: MdWorkspaceContents, - private readonly linkComputer: MdLinkComputer, + private readonly linkProvider: MdLinkProvider, ) { } - public async getDiagnostics(doc: SkinnyTextDocument, options: DiagnosticOptions, token: vscode.CancellationToken): Promise<{ readonly diagnostics: vscode.Diagnostic[]; readonly links: MdLink[] }> { - const links = await this.linkComputer.getAllLinks(doc, token); + public async getDiagnostics(doc: SkinnyTextDocument, options: DiagnosticOptions, token: vscode.CancellationToken): Promise<{ readonly diagnostics: vscode.Diagnostic[]; readonly links: readonly MdLink[] }> { + const { links, definitions } = await this.linkProvider.getLinks(doc); if (token.isCancellationRequested || !options.enabled) { return { links, diagnostics: [] }; } @@ -463,7 +463,7 @@ export class DiagnosticComputer { links, diagnostics: (await Promise.all([ this.validateFileLinks(options, links, token), - Array.from(this.validateReferenceLinks(options, links)), + Array.from(this.validateReferenceLinks(options, links, definitions)), this.validateFragmentLinks(doc, options, links, token), ])).flat() }; @@ -501,15 +501,14 @@ export class DiagnosticComputer { return diagnostics; } - private *validateReferenceLinks(options: DiagnosticOptions, links: readonly MdLink[]): Iterable { + private *validateReferenceLinks(options: DiagnosticOptions, links: readonly MdLink[], definitions: LinkDefinitionSet): Iterable { const severity = toSeverity(options.validateReferences); if (typeof severity === 'undefined') { return []; } - const definitionSet = new LinkDefinitionSet(links); for (const link of links) { - if (link.href.kind === 'reference' && !definitionSet.lookup(link.href.ref)) { + if (link.href.kind === 'reference' && !definitions.lookup(link.href.ref)) { yield new vscode.Diagnostic( link.source.hrefRange, localize('invalidReferenceLink', 'No link definition found: \'{0}\'', link.href.ref), @@ -620,19 +619,19 @@ class AddToIgnoreLinksQuickFixProvider implements vscode.CodeActionProvider { } } -export function register( +export function registerDiagnosticSupport( selector: vscode.DocumentSelector, engine: MarkdownEngine, workspaceContents: MdWorkspaceContents, - linkComputer: MdLinkComputer, + linkProvider: MdLinkProvider, commandManager: CommandManager, - referenceComputer: MdReferencesComputer, + referenceComputer: MdReferencesProvider, ): vscode.Disposable { const configuration = new VSCodeDiagnosticConfiguration(); const manager = new DiagnosticManager( engine, workspaceContents, - new DiagnosticComputer(engine, workspaceContents, linkComputer), + new DiagnosticComputer(engine, workspaceContents, linkProvider), configuration, new DiagnosticCollectionReporter(), referenceComputer); diff --git a/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts b/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts index 5331240e295..ab7d3b14bcc 100644 --- a/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts @@ -9,8 +9,11 @@ import * as uri from 'vscode-uri'; import { OpenDocumentLinkCommand } from '../commands/openDocumentLink'; import { MarkdownEngine } from '../markdownEngine'; import { coalesce } from '../util/arrays'; +import { noopToken } from '../util/cancellation'; +import { Disposable } from '../util/dispose'; import { getUriForLinkWithKnownExternalScheme, isOfScheme, Schemes } from '../util/schemes'; -import { SkinnyTextDocument } from '../workspaceContents'; +import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; +import { MdDocumentInfoCache } from './workspaceCache'; const localize = nls.loadMessageBundle(); @@ -242,6 +245,9 @@ class NoLinkRanges { } } +/** + * Stateless object that extracts link information from markdown files. + */ export class MdLinkComputer { constructor( @@ -257,7 +263,7 @@ export class MdLinkComputer { return Array.from([ ...this.getInlineLinks(document, noLinkRanges), ...this.getReferenceLinks(document, noLinkRanges), - ...this.getLinkDefinitions2(document, noLinkRanges), + ...this.getLinkDefinitions(document, noLinkRanges), ...this.getAutoLinks(document, noLinkRanges), ]); } @@ -369,12 +375,7 @@ export class MdLinkComputer { } } - public async getLinkDefinitions(document: SkinnyTextDocument): Promise> { - const noLinkRanges = await NoLinkRanges.compute(document, this.engine); - return this.getLinkDefinitions2(document, noLinkRanges); - } - - private *getLinkDefinitions2(document: SkinnyTextDocument, noLinkRanges: NoLinkRanges): Iterable { + private *getLinkDefinitions(document: SkinnyTextDocument, noLinkRanges: NoLinkRanges): Iterable { const text = document.getText(); for (const match of text.matchAll(definitionPattern)) { const pre = match[1]; @@ -419,7 +420,37 @@ export class MdLinkComputer { } } -export class LinkDefinitionSet { +/** + * Stateful object which provides links for markdown files the workspace. + */ +export class MdLinkProvider extends Disposable { + + private readonly _linkCache: MdDocumentInfoCache; + + private readonly linkComputer: MdLinkComputer; + + constructor( + engine: MarkdownEngine, + workspaceContents: MdWorkspaceContents, + ) { + super(); + this.linkComputer = new MdLinkComputer(engine); + this._linkCache = this._register(new MdDocumentInfoCache(workspaceContents, doc => this.linkComputer.getAllLinks(doc, noopToken))); + } + + public async getLinks(document: SkinnyTextDocument): Promise<{ + readonly links: readonly MdLink[]; + readonly definitions: LinkDefinitionSet; + }> { + const links = (await this._linkCache.get(document.uri)) ?? []; + return { + links, + definitions: new LinkDefinitionSet(links), + }; + } +} + +export class LinkDefinitionSet implements Iterable<[string, MdLinkDefinition]> { private readonly _map = new Map(); constructor(links: Iterable) { @@ -430,29 +461,31 @@ export class LinkDefinitionSet { } } + public [Symbol.iterator](): Iterator<[string, MdLinkDefinition]> { + return this._map.entries(); + } + public lookup(ref: string): MdLinkDefinition | undefined { return this._map.get(ref); } } -export class MdLinkProvider implements vscode.DocumentLinkProvider { +export class MdVsCodeLinkProvider implements vscode.DocumentLinkProvider { constructor( - private readonly _linkComputer: MdLinkComputer, + private readonly _linkProvider: MdLinkProvider, ) { } public async provideDocumentLinks( document: SkinnyTextDocument, token: vscode.CancellationToken ): Promise { - const allLinks = (await this._linkComputer.getAllLinks(document, token)) ?? []; + const { links, definitions } = await this._linkProvider.getLinks(document); if (token.isCancellationRequested) { return []; } - const definitionSet = new LinkDefinitionSet(allLinks); - return coalesce(allLinks - .map(data => this.toValidDocumentLink(data, definitionSet))); + return coalesce(links.map(data => this.toValidDocumentLink(data, definitions))); } private toValidDocumentLink(link: MdLink, definitionSet: LinkDefinitionSet): vscode.DocumentLink | undefined { @@ -482,9 +515,9 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider { } } -export function registerDocumentLinkProvider( +export function registerDocumentLinkSupport( selector: vscode.DocumentSelector, - linkComputer: MdLinkComputer, + linkProvider: MdLinkProvider, ): vscode.Disposable { - return vscode.languages.registerDocumentLinkProvider(selector, new MdLinkProvider(linkComputer)); + return vscode.languages.registerDocumentLinkProvider(selector, new MdVsCodeLinkProvider(linkProvider)); } diff --git a/extensions/markdown-language-features/src/languageFeatures/documentSymbolProvider.ts b/extensions/markdown-language-features/src/languageFeatures/documentSymbolProvider.ts index cdb1b8a79b8..ba89c2d1046 100644 --- a/extensions/markdown-language-features/src/languageFeatures/documentSymbolProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/documentSymbolProvider.ts @@ -74,3 +74,10 @@ export class MdDocumentSymbolProvider implements vscode.DocumentSymbolProvider { return '#'.repeat(entry.level) + ' ' + entry.text; } } + +export function registerDocumentSymbolSupport( + selector: vscode.DocumentSelector, + engine: MarkdownEngine, +): vscode.Disposable { + return vscode.languages.registerDocumentSymbolProvider(selector, new MdDocumentSymbolProvider(engine)); +} diff --git a/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts b/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts index 7217db35652..0947e16840e 100644 --- a/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts +++ b/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts @@ -23,7 +23,7 @@ const imageFileExtensions = new Set([ '.webp', ]); -export function registerDropIntoEditor(selector: vscode.DocumentSelector) { +export function registerDropIntoEditorSupport(selector: vscode.DocumentSelector) { return vscode.languages.registerDocumentOnDropEditProvider(selector, new class implements vscode.DocumentOnDropEditProvider { async provideDocumentOnDropEdits(document: vscode.TextDocument, _position: vscode.Position, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise { const enabled = vscode.workspace.getConfiguration('markdown', document).get('editor.drop.enabled', true); diff --git a/extensions/markdown-language-features/src/languageFeatures/fileReferences.ts b/extensions/markdown-language-features/src/languageFeatures/fileReferences.ts index d6281d9c0ca..9a2ecb9aa8e 100644 --- a/extensions/markdown-language-features/src/languageFeatures/fileReferences.ts +++ b/extensions/markdown-language-features/src/languageFeatures/fileReferences.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import { Command, CommandManager } from '../commandManager'; -import { MdReferencesComputer } from './references'; +import { MdReferencesProvider } from './references'; const localize = nls.loadMessageBundle(); @@ -16,7 +16,7 @@ export class FindFileReferencesCommand implements Command { public readonly id = 'markdown.findAllFileReferences'; constructor( - private readonly referencesComputer: MdReferencesComputer, + private readonly referencesProvider: MdReferencesProvider, ) { } public async execute(resource?: vscode.Uri) { @@ -33,7 +33,7 @@ export class FindFileReferencesCommand implements Command { location: vscode.ProgressLocation.Window, title: localize('progress.title', "Finding file references") }, async (_progress, token) => { - const references = await this.referencesComputer.getAllReferencesToFile(resource!, token); + const references = await this.referencesProvider.getAllReferencesToFile(resource!, token); const locations = references.map(ref => ref.location); const config = vscode.workspace.getConfiguration('references'); @@ -49,6 +49,9 @@ export class FindFileReferencesCommand implements Command { } } -export function registerFindFileReferences(commandManager: CommandManager, referencesProvider: MdReferencesComputer): vscode.Disposable { +export function registerFindFileReferenceSupport( + commandManager: CommandManager, + referencesProvider: MdReferencesProvider +): vscode.Disposable { return commandManager.register(new FindFileReferencesCommand(referencesProvider)); } diff --git a/extensions/markdown-language-features/src/languageFeatures/foldingProvider.ts b/extensions/markdown-language-features/src/languageFeatures/foldingProvider.ts index 243a2ee12fd..13675908722 100644 --- a/extensions/markdown-language-features/src/languageFeatures/foldingProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/foldingProvider.ts @@ -111,3 +111,10 @@ const isFoldableToken = (token: Token): token is MarkdownItTokenWithMap => { return false; } }; + +export function registerFoldingSupport( + selector: vscode.DocumentSelector, + engine: MarkdownEngine, +): vscode.Disposable { + return vscode.languages.registerFoldingRangeProvider(selector, new MdFoldingProvider(engine)); +} diff --git a/extensions/markdown-language-features/src/languageFeatures/pathCompletions.ts b/extensions/markdown-language-features/src/languageFeatures/pathCompletions.ts index 1e3824da90d..b9a59ca4cec 100644 --- a/extensions/markdown-language-features/src/languageFeatures/pathCompletions.ts +++ b/extensions/markdown-language-features/src/languageFeatures/pathCompletions.ts @@ -9,7 +9,7 @@ import { MarkdownEngine } from '../markdownEngine'; import { TableOfContents } from '../tableOfContents'; import { resolveUriToMarkdownFile } from '../util/openDocumentLink'; import { SkinnyTextDocument } from '../workspaceContents'; -import { MdLinkComputer } from './documentLinkProvider'; +import { MdLinkProvider } from './documentLinkProvider'; enum CompletionContextKind { /** `[...](|)` */ @@ -76,19 +76,14 @@ function tryDecodeUriComponent(str: string): string { } } -export class MdPathCompletionProvider implements vscode.CompletionItemProvider { - - public static register( - selector: vscode.DocumentSelector, - engine: MarkdownEngine, - linkComputer: MdLinkComputer, - ): vscode.Disposable { - return vscode.languages.registerCompletionItemProvider(selector, new MdPathCompletionProvider(engine, linkComputer), '.', '/', '#'); - } +/** + * Adds path completions in markdown files by implementing {@link vscode.CompletionItemProvider}. + */ +export class MdVsCodePathCompletionProvider implements vscode.CompletionItemProvider { constructor( private readonly engine: MarkdownEngine, - private readonly linkComputer: MdLinkComputer, + private readonly linkProvider: MdLinkProvider, ) { } public async provideCompletionItems(document: SkinnyTextDocument, position: vscode.Position, _token: vscode.CancellationToken, _context: vscode.CompletionContext): Promise { @@ -240,8 +235,8 @@ export class MdPathCompletionProvider implements vscode.CompletionItemProvider { const insertionRange = new vscode.Range(context.linkTextStartPosition, position); const replacementRange = new vscode.Range(insertionRange.start, position.translate({ characterDelta: context.linkSuffix.length })); - const definitions = await this.linkComputer.getLinkDefinitions(document); - for (const def of definitions) { + const { definitions } = await this.linkProvider.getLinks(document); + for (const [_, def] of definitions) { yield { kind: vscode.CompletionItemKind.Reference, label: def.ref.text, @@ -351,3 +346,11 @@ export class MdPathCompletionProvider implements vscode.CompletionItemProvider { return document.uri; } } + +export function registerPathCompletionSupport( + selector: vscode.DocumentSelector, + engine: MarkdownEngine, + linkProvider: MdLinkProvider, +): vscode.Disposable { + return vscode.languages.registerCompletionItemProvider(selector, new MdVsCodePathCompletionProvider(engine, linkProvider), '.', '/', '#'); +} diff --git a/extensions/markdown-language-features/src/languageFeatures/references.ts b/extensions/markdown-language-features/src/languageFeatures/references.ts index 8e1640b60f4..d386204a333 100644 --- a/extensions/markdown-language-features/src/languageFeatures/references.ts +++ b/extensions/markdown-language-features/src/languageFeatures/references.ts @@ -5,13 +5,12 @@ import * as vscode from 'vscode'; import * as uri from 'vscode-uri'; import { MarkdownEngine } from '../markdownEngine'; -import { Slugifier } from '../slugify'; import { TableOfContents, TocEntry } from '../tableOfContents'; import { noopToken } from '../util/cancellation'; import { Disposable } from '../util/dispose'; import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; import { InternalHref, MdLink, MdLinkComputer } from './documentLinkProvider'; -import { MdWorkspaceCache } from './workspaceCache'; +import { MdWorkspaceInfoCache } from './workspaceCache'; /** @@ -59,19 +58,22 @@ export interface MdHeaderReference { export type MdReference = MdLinkReference | MdHeaderReference; -export class MdReferencesComputer extends Disposable { +/** + * Stateful object that computes references for markdown files. + */ +export class MdReferencesProvider extends Disposable { - private readonly _linkCache: MdWorkspaceCache; + private readonly _linkCache: MdWorkspaceInfoCache; + private readonly _linkComputer: MdLinkComputer; public constructor( - private readonly linkComputer: MdLinkComputer, - private readonly workspaceContents: MdWorkspaceContents, private readonly engine: MarkdownEngine, - private readonly slugifier: Slugifier, + private readonly workspaceContents: MdWorkspaceContents, ) { super(); - this._linkCache = this._register(new MdWorkspaceCache(workspaceContents, doc => linkComputer.getAllLinks(doc, noopToken))); + this._linkComputer = new MdLinkComputer(engine); + this._linkCache = this._register(new MdWorkspaceInfoCache(workspaceContents, doc => this._linkComputer.getAllLinks(doc, noopToken))); } public async getReferencesAtPosition(document: SkinnyTextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { @@ -110,7 +112,7 @@ export class MdReferencesComputer extends Disposable { for (const link of links) { if (link.href.kind === 'internal' && this.looksLikeLinkToDoc(link.href, document.uri) - && this.slugifier.fromHeading(link.href.fragment).value === header.slug.value + && this.engine.slugifier.fromHeading(link.href.fragment).value === header.slug.value ) { references.push({ kind: 'link', @@ -126,7 +128,7 @@ export class MdReferencesComputer extends Disposable { } private async getReferencesToLinkAtPosition(document: SkinnyTextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { - const docLinks = await this.linkComputer.getAllLinks(document, token); + const docLinks = await this._linkComputer.getAllLinks(document, token); for (const link of docLinks) { if (link.kind === 'definition') { @@ -200,7 +202,7 @@ export class MdReferencesComputer extends Disposable { continue; } - if (this.slugifier.fromHeading(link.href.fragment).equals(this.slugifier.fromHeading(sourceLink.href.fragment))) { + if (this.engine.slugifier.fromHeading(link.href.fragment).equals(this.engine.slugifier.fromHeading(sourceLink.href.fragment))) { const isTriggerLocation = sourceLink.source.resource.fsPath === link.source.resource.fsPath && sourceLink.source.hrefRange.isEqual(link.source.hrefRange); references.push({ kind: 'link', @@ -284,27 +286,27 @@ export class MdReferencesComputer extends Disposable { } /** - * + * Implements {@link vscode.ReferenceProvider} for markdown documents. */ export class MdVsCodeReferencesProvider implements vscode.ReferenceProvider { public constructor( - private readonly referencesComputer: MdReferencesComputer + private readonly referencesProvider: MdReferencesProvider ) { } async provideReferences(document: SkinnyTextDocument, position: vscode.Position, context: vscode.ReferenceContext, token: vscode.CancellationToken): Promise { - const allRefs = await this.referencesComputer.getReferencesAtPosition(document, position, token); + const allRefs = await this.referencesProvider.getReferencesAtPosition(document, position, token); return allRefs .filter(ref => context.includeDeclaration || !ref.isDefinition) .map(ref => ref.location); } } -export function registerReferencesProvider( +export function registerReferencesSupport( selector: vscode.DocumentSelector, - computer: MdReferencesComputer, + referencesProvider: MdReferencesProvider, ): vscode.Disposable { - return vscode.languages.registerReferenceProvider(selector, new MdVsCodeReferencesProvider(computer)); + return vscode.languages.registerReferenceProvider(selector, new MdVsCodeReferencesProvider(referencesProvider)); } export async function tryFindMdDocumentForLink(href: InternalHref, workspaceContents: MdWorkspaceContents): Promise { diff --git a/extensions/markdown-language-features/src/languageFeatures/rename.ts b/extensions/markdown-language-features/src/languageFeatures/rename.ts index 2fe96c7316c..ef7527a26b2 100644 --- a/extensions/markdown-language-features/src/languageFeatures/rename.ts +++ b/extensions/markdown-language-features/src/languageFeatures/rename.ts @@ -11,7 +11,7 @@ import { Disposable } from '../util/dispose'; import { resolveDocumentLink } from '../util/openDocumentLink'; import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; import { InternalHref } from './documentLinkProvider'; -import { MdHeaderReference, MdLinkReference, MdReference, MdReferencesComputer, tryFindMdDocumentForLink } from './references'; +import { MdHeaderReference, MdLinkReference, MdReference, MdReferencesProvider, tryFindMdDocumentForLink } from './references'; const localize = nls.loadMessageBundle(); @@ -45,7 +45,7 @@ function tryDecodeUri(str: string): string { } } -export class MdRenameProvider extends Disposable implements vscode.RenameProvider { +export class MdVsCodeRenameProvider extends Disposable implements vscode.RenameProvider { private cachedRefs?: { readonly resource: vscode.Uri; @@ -58,8 +58,8 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide private readonly renameNotSupportedText = localize('invalidRenameLocation', "Rename not supported at location"); public constructor( - private readonly referencesComputer: MdReferencesComputer, private readonly workspaceContents: MdWorkspaceContents, + private readonly referencesProvider: MdReferencesProvider, private readonly slugifier: Slugifier, ) { super(); @@ -253,7 +253,7 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide return this.cachedRefs; } - const references = await this.referencesComputer.getReferencesAtPosition(document, position, token); + const references = await this.referencesProvider.getReferencesAtPosition(document, position, token); const triggerRef = references.find(ref => ref.isTriggerLocation); if (!triggerRef) { return undefined; @@ -270,3 +270,12 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide } } + +export function registerRenameSupport( + selector: vscode.DocumentSelector, + workspaceContents: MdWorkspaceContents, + referencesProvider: MdReferencesProvider, + slugifier: Slugifier, +): vscode.Disposable { + return vscode.languages.registerRenameProvider(selector, new MdVsCodeRenameProvider(workspaceContents, referencesProvider, slugifier)); +} diff --git a/extensions/markdown-language-features/src/languageFeatures/smartSelect.ts b/extensions/markdown-language-features/src/languageFeatures/smartSelect.ts index 3f69ea546d7..09cdd4acfce 100644 --- a/extensions/markdown-language-features/src/languageFeatures/smartSelect.ts +++ b/extensions/markdown-language-features/src/languageFeatures/smartSelect.ts @@ -249,3 +249,10 @@ function getFirstChildHeader(document: SkinnyTextDocument, header?: TocEntry, to } return undefined; } + +export function registerSmartSelectSupport( + selector: vscode.DocumentSelector, + engine: MarkdownEngine, +): vscode.Disposable { + return vscode.languages.registerSelectionRangeProvider(selector, new MdSmartSelect(engine)); +} diff --git a/extensions/markdown-language-features/src/languageFeatures/workspaceCache.ts b/extensions/markdown-language-features/src/languageFeatures/workspaceCache.ts index 295771bfa60..50ef9ebef52 100644 --- a/extensions/markdown-language-features/src/languageFeatures/workspaceCache.ts +++ b/extensions/markdown-language-features/src/languageFeatures/workspaceCache.ts @@ -9,12 +9,89 @@ import { Lazy, lazy } from '../util/lazy'; import { ResourceMap } from '../util/resourceMap'; import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; -/** - * Cache of information for markdown files in the workspace. - */ -export class MdWorkspaceCache extends Disposable { +class LazyResourceMap { + private readonly _map = new ResourceMap>>(); - private readonly _cache = new ResourceMap>>(); + public has(resource: vscode.Uri): boolean { + return this._map.has(resource); + } + + public get(resource: vscode.Uri): Promise | undefined { + return this._map.get(resource)?.value; + } + + public set(resource: vscode.Uri, value: Lazy>) { + this._map.set(resource, value); + } + + public delete(resource: vscode.Uri) { + this._map.delete(resource); + } + + public entries(): Promise> { + return Promise.all(Array.from(this._map.entries(), async ([key, entry]) => { + return [key, await entry.value]; + })); + } +} + +/** + * Cache of information per-document in the workspace. + * + * The values are computed lazily and invalidated when the document changes. + */ +export class MdDocumentInfoCache extends Disposable { + + private readonly _cache = new LazyResourceMap(); + + public constructor( + private readonly workspaceContents: MdWorkspaceContents, + private readonly getValue: (document: SkinnyTextDocument) => Promise, + ) { + super(); + + this._register(this.workspaceContents.onDidChangeMarkdownDocument(doc => this.onDidChangeDocument(doc))); + this._register(this.workspaceContents.onDidCreateMarkdownDocument(doc => this.onDidChangeDocument(doc))); + this._register(this.workspaceContents.onDidDeleteMarkdownDocument(this.onDidDeleteDocument, this)); + } + + public async get(resource: vscode.Uri): Promise { + const existing = this._cache.get(resource); + if (existing) { + return existing; + } + + const doc = await this.workspaceContents.getMarkdownDocument(resource); + return doc && this.onDidChangeDocument(doc, true)?.value; + } + + public async entries(): Promise> { + return this._cache.entries(); + } + + private onDidChangeDocument(document: SkinnyTextDocument, forceAdd = false): Lazy> | undefined { + if (forceAdd || this._cache.has(document.uri)) { + const value = lazy(() => this.getValue(document)); + this._cache.set(document.uri, value); + return value; + } + return undefined; + } + + private onDidDeleteDocument(resource: vscode.Uri) { + this._cache.delete(resource); + } +} + +/** + * Cache of information across all markdown files in the workspace. + * + * Unlike {@link MdDocumentInfoCache}, the entries here are computed eagerly for every file in the workspace. + * However the computation of the values is still lazy. + */ +export class MdWorkspaceInfoCache extends Disposable { + + private readonly _cache = new LazyResourceMap(); private _init?: Promise; public constructor( @@ -26,14 +103,12 @@ export class MdWorkspaceCache extends Disposable { public async entries(): Promise> { await this.ensureInit(); - return Promise.all(Array.from(this._cache.entries(), async ([key, entry]) => { - return [key, await entry.value]; - })); + return this._cache.entries(); } public async values(): Promise> { await this.ensureInit(); - return Promise.all(Array.from(this._cache.values(), x => x.value)); + return Array.from(await this._cache.entries(), x => x[1]); } private async ensureInit(): Promise { diff --git a/extensions/markdown-language-features/src/languageFeatures/workspaceSymbolProvider.ts b/extensions/markdown-language-features/src/languageFeatures/workspaceSymbolProvider.ts index 5906cc1cc72..41734606ffa 100644 --- a/extensions/markdown-language-features/src/languageFeatures/workspaceSymbolProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/workspaceSymbolProvider.ts @@ -7,11 +7,11 @@ import * as vscode from 'vscode'; import { Disposable } from '../util/dispose'; import { MdWorkspaceContents } from '../workspaceContents'; import { MdDocumentSymbolProvider } from './documentSymbolProvider'; -import { MdWorkspaceCache } from './workspaceCache'; +import { MdWorkspaceInfoCache } from './workspaceCache'; export class MdWorkspaceSymbolProvider extends Disposable implements vscode.WorkspaceSymbolProvider { - private readonly _cache: MdWorkspaceCache; + private readonly _cache: MdWorkspaceInfoCache; public constructor( symbolProvider: MdDocumentSymbolProvider, @@ -19,7 +19,7 @@ export class MdWorkspaceSymbolProvider extends Disposable implements vscode.Work ) { super(); - this._cache = this._register(new MdWorkspaceCache(workspaceContents, doc => symbolProvider.provideDocumentSymbolInformation(doc))); + this._cache = this._register(new MdWorkspaceInfoCache(workspaceContents, doc => symbolProvider.provideDocumentSymbolInformation(doc))); } public async provideWorkspaceSymbols(query: string): Promise { @@ -27,3 +27,10 @@ export class MdWorkspaceSymbolProvider extends Disposable implements vscode.Work return allSymbols.filter(symbolInformation => symbolInformation.name.toLowerCase().indexOf(query.toLowerCase()) !== -1); } } + +export function registerWorkspaceSymbolSupport( + workspaceContents: MdWorkspaceContents, + symbolProvider: MdDocumentSymbolProvider, +): vscode.Disposable { + return vscode.languages.registerWorkspaceSymbolProvider(new MdWorkspaceSymbolProvider(symbolProvider, workspaceContents)); +} diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 3f32880d22f..400132d781f 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -98,10 +98,14 @@ export class MarkdownEngine { private _slugCount = new Map(); private _tokenCache = new TokenCache(); + public readonly slugifier: Slugifier; + public constructor( private readonly contributionProvider: MarkdownContributionProvider, - private readonly slugifier: Slugifier, + slugifier: Slugifier, ) { + this.slugifier = slugifier; + contributionProvider.onContributionsChanged(() => { // Markdown plugin contributions may have changed this.md = undefined; diff --git a/extensions/markdown-language-features/src/test/definitionProvider.test.ts b/extensions/markdown-language-features/src/test/definitionProvider.test.ts index 7de40fc0f58..92310565793 100644 --- a/extensions/markdown-language-features/src/test/definitionProvider.test.ts +++ b/extensions/markdown-language-features/src/test/definitionProvider.test.ts @@ -7,9 +7,7 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; import { MdDefinitionProvider } from '../languageFeatures/definitionProvider'; -import { MdLinkComputer } from '../languageFeatures/documentLinkProvider'; -import { MdReferencesComputer } from '../languageFeatures/references'; -import { githubSlugifier } from '../slugify'; +import { MdReferencesProvider } from '../languageFeatures/references'; import { noopToken } from '../util/cancellation'; import { InMemoryDocument } from '../util/inMemoryDocument'; import { MdWorkspaceContents } from '../workspaceContents'; @@ -18,11 +16,10 @@ import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; import { joinLines, workspacePath } from './util'; -function getDefinition(doc: InMemoryDocument, pos: vscode.Position, workspaceContents: MdWorkspaceContents) { +function getDefinition(doc: InMemoryDocument, pos: vscode.Position, workspace: MdWorkspaceContents) { const engine = createNewMarkdownEngine(); - const linkComputer = new MdLinkComputer(engine); - const referencesComputer = new MdReferencesComputer(linkComputer, workspaceContents, engine, githubSlugifier); - const provider = new MdDefinitionProvider(referencesComputer); + const referencesProvider = new MdReferencesProvider(engine, workspace); + const provider = new MdDefinitionProvider(referencesProvider); return provider.provideDefinition(doc, pos, noopToken); } diff --git a/extensions/markdown-language-features/src/test/diagnostic.test.ts b/extensions/markdown-language-features/src/test/diagnostic.test.ts index e98dd6fbd0a..e677d8ee823 100644 --- a/extensions/markdown-language-features/src/test/diagnostic.test.ts +++ b/extensions/markdown-language-features/src/test/diagnostic.test.ts @@ -7,9 +7,8 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; import { DiagnosticCollectionReporter, DiagnosticComputer, DiagnosticConfiguration, DiagnosticLevel, DiagnosticManager, DiagnosticOptions, DiagnosticReporter } from '../languageFeatures/diagnostics'; -import { MdLinkComputer } from '../languageFeatures/documentLinkProvider'; -import { MdReferencesComputer } from '../languageFeatures/references'; -import { githubSlugifier } from '../slugify'; +import { MdLinkProvider } from '../languageFeatures/documentLinkProvider'; +import { MdReferencesProvider } from '../languageFeatures/references'; import { noopToken } from '../util/cancellation'; import { disposeAll } from '../util/dispose'; import { InMemoryDocument } from '../util/inMemoryDocument'; @@ -28,10 +27,10 @@ const defaultDiagnosticsOptions = Object.freeze({ ignoreLinks: [], }); -async function getComputedDiagnostics(doc: InMemoryDocument, workspaceContents: MdWorkspaceContents, options: Partial = {}): Promise { +async function getComputedDiagnostics(doc: InMemoryDocument, workspace: MdWorkspaceContents, options: Partial = {}): Promise { const engine = createNewMarkdownEngine(); - const linkComputer = new MdLinkComputer(engine); - const computer = new DiagnosticComputer(engine, workspaceContents, linkComputer); + const linkProvider = new MdLinkProvider(engine, workspace); + const computer = new DiagnosticComputer(engine, workspace, linkProvider); return ( await computer.getDiagnostics(doc, { ...defaultDiagnosticsOptions, ...options, }, noopToken) ).diagnostics; @@ -430,17 +429,17 @@ suite('Markdown: Diagnostics manager', () => { reporter: DiagnosticReporter = new DiagnosticCollectionReporter(), ) { const engine = createNewMarkdownEngine(); - const linkComputer = new MdLinkComputer(engine); - const referencesComputer = new MdReferencesComputer(linkComputer, workspace, engine, githubSlugifier); + const linkProvider = new MdLinkProvider(engine, workspace); + const referencesProvider = new MdReferencesProvider(engine, workspace); const manager = new DiagnosticManager( engine, workspace, - new DiagnosticComputer(engine, workspace, linkComputer), + new DiagnosticComputer(engine, workspace, linkProvider), configuration, reporter, - referencesComputer, + referencesProvider, 0); - _disposables.push(manager, referencesComputer); + _disposables.push(manager, referencesProvider); return manager; } diff --git a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts index 899641258d9..78c1ceee590 100644 --- a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts +++ b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts @@ -6,19 +6,21 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; -import { MdLinkComputer, MdLinkProvider } from '../languageFeatures/documentLinkProvider'; +import { MdLinkProvider, MdVsCodeLinkProvider } from '../languageFeatures/documentLinkProvider'; import { noopToken } from '../util/cancellation'; import { InMemoryDocument } from '../util/inMemoryDocument'; import { createNewMarkdownEngine } from './engine'; -import { assertRangeEqual, joinLines } from './util'; +import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; +import { assertRangeEqual, joinLines, workspacePath } from './util'; -const testFile = vscode.Uri.joinPath(vscode.workspace.workspaceFolders![0].uri, 'x.md'); - function getLinksForFile(fileContents: string) { - const doc = new InMemoryDocument(testFile, fileContents); - const linkComputer = new MdLinkComputer(createNewMarkdownEngine()); - const provider = new MdLinkProvider(linkComputer); + const doc = new InMemoryDocument(workspacePath('x.md'), fileContents); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc]); + + const engine = createNewMarkdownEngine(); + const linkProvider = new MdLinkProvider(engine, workspace); + const provider = new MdVsCodeLinkProvider(linkProvider); return provider.provideDocumentLinks(doc, noopToken); } diff --git a/extensions/markdown-language-features/src/test/documentSymbolProvider.test.ts b/extensions/markdown-language-features/src/test/documentSymbolProvider.test.ts index e29c7399e7a..1d09a441c08 100644 --- a/extensions/markdown-language-features/src/test/documentSymbolProvider.test.ts +++ b/extensions/markdown-language-features/src/test/documentSymbolProvider.test.ts @@ -5,22 +5,18 @@ import * as assert from 'assert'; import 'mocha'; -import * as vscode from 'vscode'; import { MdDocumentSymbolProvider } from '../languageFeatures/documentSymbolProvider'; import { createNewMarkdownEngine } from './engine'; import { InMemoryDocument } from '../util/inMemoryDocument'; - - -const testFileName = vscode.Uri.file('test.md'); +import { workspacePath } from './util'; function getSymbolsForFile(fileContents: string) { - const doc = new InMemoryDocument(testFileName, fileContents); + const doc = new InMemoryDocument(workspacePath('test.md'), fileContents); const provider = new MdDocumentSymbolProvider(createNewMarkdownEngine()); return provider.provideDocumentSymbols(doc); } - suite('markdown.DocumentSymbolProvider', () => { test('Should not return anything for empty document', async () => { const symbols = await getSymbolsForFile(''); diff --git a/extensions/markdown-language-features/src/test/fileReferences.test.ts b/extensions/markdown-language-features/src/test/fileReferences.test.ts index d160f1efdca..2b936b1caa9 100644 --- a/extensions/markdown-language-features/src/test/fileReferences.test.ts +++ b/extensions/markdown-language-features/src/test/fileReferences.test.ts @@ -6,9 +6,7 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; -import { MdLinkComputer } from '../languageFeatures/documentLinkProvider'; -import { MdReference, MdReferencesComputer } from '../languageFeatures/references'; -import { githubSlugifier } from '../slugify'; +import { MdReference, MdReferencesProvider } from '../languageFeatures/references'; import { noopToken } from '../util/cancellation'; import { InMemoryDocument } from '../util/inMemoryDocument'; import { MdWorkspaceContents } from '../workspaceContents'; @@ -19,8 +17,7 @@ import { joinLines, workspacePath } from './util'; function getFileReferences(resource: vscode.Uri, workspaceContents: MdWorkspaceContents) { const engine = createNewMarkdownEngine(); - const linkComputer = new MdLinkComputer(engine); - const computer = new MdReferencesComputer(linkComputer, workspaceContents, engine, githubSlugifier); + const computer = new MdReferencesProvider(engine, workspaceContents); return computer.getAllReferencesToFile(resource, noopToken); } diff --git a/extensions/markdown-language-features/src/test/pathCompletion.test.ts b/extensions/markdown-language-features/src/test/pathCompletion.test.ts index f50bb9126af..0cc98a65453 100644 --- a/extensions/markdown-language-features/src/test/pathCompletion.test.ts +++ b/extensions/markdown-language-features/src/test/pathCompletion.test.ts @@ -6,19 +6,22 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; -import { MdLinkComputer } from '../languageFeatures/documentLinkProvider'; -import { MdPathCompletionProvider } from '../languageFeatures/pathCompletions'; +import { MdLinkProvider } from '../languageFeatures/documentLinkProvider'; +import { MdVsCodePathCompletionProvider } from '../languageFeatures/pathCompletions'; import { noopToken } from '../util/cancellation'; import { InMemoryDocument } from '../util/inMemoryDocument'; import { createNewMarkdownEngine } from './engine'; +import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; import { CURSOR, getCursorPositions, joinLines, workspacePath } from './util'; function getCompletionsAtCursor(resource: vscode.Uri, fileContents: string) { const doc = new InMemoryDocument(resource, fileContents); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc]); + const engine = createNewMarkdownEngine(); - const linkComputer = new MdLinkComputer(engine); - const provider = new MdPathCompletionProvider(engine, linkComputer); + const linkProvider = new MdLinkProvider(engine, workspace); + const provider = new MdVsCodePathCompletionProvider(engine, linkProvider); const cursorPositions = getCursorPositions(fileContents, doc); return provider.provideCompletionItems(doc, cursorPositions[0], noopToken, { triggerCharacter: undefined, diff --git a/extensions/markdown-language-features/src/test/references.test.ts b/extensions/markdown-language-features/src/test/references.test.ts index fe93dacb2ab..c9a9e53b3d1 100644 --- a/extensions/markdown-language-features/src/test/references.test.ts +++ b/extensions/markdown-language-features/src/test/references.test.ts @@ -6,9 +6,7 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; -import { MdLinkComputer } from '../languageFeatures/documentLinkProvider'; -import { MdReferencesComputer, MdVsCodeReferencesProvider } from '../languageFeatures/references'; -import { githubSlugifier } from '../slugify'; +import { MdReferencesProvider, MdVsCodeReferencesProvider } from '../languageFeatures/references'; import { noopToken } from '../util/cancellation'; import { InMemoryDocument } from '../util/inMemoryDocument'; import { MdWorkspaceContents } from '../workspaceContents'; @@ -19,8 +17,7 @@ import { joinLines, workspacePath } from './util'; function getReferences(doc: InMemoryDocument, pos: vscode.Position, workspaceContents: MdWorkspaceContents) { const engine = createNewMarkdownEngine(); - const linkComputer = new MdLinkComputer(engine); - const computer = new MdReferencesComputer(linkComputer, workspaceContents, engine, githubSlugifier); + const computer = new MdReferencesProvider(engine, workspaceContents); const provider = new MdVsCodeReferencesProvider(computer); return provider.provideReferences(doc, pos, { includeDeclaration: true }, noopToken); } diff --git a/extensions/markdown-language-features/src/test/rename.test.ts b/extensions/markdown-language-features/src/test/rename.test.ts index 0f3e23669b9..95c420eb173 100644 --- a/extensions/markdown-language-features/src/test/rename.test.ts +++ b/extensions/markdown-language-features/src/test/rename.test.ts @@ -6,9 +6,8 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; -import { MdLinkComputer } from '../languageFeatures/documentLinkProvider'; -import { MdReferencesComputer } from '../languageFeatures/references'; -import { MdRenameProvider, MdWorkspaceEdit } from '../languageFeatures/rename'; +import { MdReferencesProvider } from '../languageFeatures/references'; +import { MdVsCodeRenameProvider, MdWorkspaceEdit } from '../languageFeatures/rename'; import { githubSlugifier } from '../slugify'; import { noopToken } from '../util/cancellation'; import { InMemoryDocument } from '../util/inMemoryDocument'; @@ -21,22 +20,20 @@ import { assertRangeEqual, joinLines, workspacePath } from './util'; /** * Get prepare rename info. */ -function prepareRename(doc: InMemoryDocument, pos: vscode.Position, workspaceContents: MdWorkspaceContents): Promise { +function prepareRename(doc: InMemoryDocument, pos: vscode.Position, workspace: MdWorkspaceContents): Promise { const engine = createNewMarkdownEngine(); - const linkComputer = new MdLinkComputer(engine); - const referenceComputer = new MdReferencesComputer(linkComputer, workspaceContents, engine, githubSlugifier); - const renameProvider = new MdRenameProvider(referenceComputer, workspaceContents, githubSlugifier); + const referenceComputer = new MdReferencesProvider(engine, workspace); + const renameProvider = new MdVsCodeRenameProvider(workspace, referenceComputer, githubSlugifier); return renameProvider.prepareRename(doc, pos, noopToken); } /** * Get all the edits for the rename. */ -function getRenameEdits(doc: InMemoryDocument, pos: vscode.Position, newName: string, workspaceContents: MdWorkspaceContents): Promise { +function getRenameEdits(doc: InMemoryDocument, pos: vscode.Position, newName: string, workspace: MdWorkspaceContents): Promise { const engine = createNewMarkdownEngine(); - const linkComputer = new MdLinkComputer(engine); - const referencesProvider = new MdReferencesComputer(linkComputer, workspaceContents, engine, githubSlugifier); - const renameProvider = new MdRenameProvider(referencesProvider, workspaceContents, githubSlugifier); + const referencesProvider = new MdReferencesProvider(engine, workspace); + const renameProvider = new MdVsCodeRenameProvider(workspace, referencesProvider, githubSlugifier); return renameProvider.provideRenameEditsImpl(doc, pos, newName, noopToken); } From 626fd8a6c7fe6018cff43ce363a7b4c0f46984fd Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 17 Jun 2022 10:32:53 +0200 Subject: [PATCH 134/175] fix #152240 (#152431) --- src/vs/workbench/browser/parts/editor/editor.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 2385271301b..18bc56414ed 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -349,7 +349,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_DIFF_SID MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: SHOW_EDITORS_IN_GROUP, title: localize('showOpenedEditors', "Show Opened Editors") }, group: '3_open', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: localize('closeAll', "Close All") }, group: '5_close', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: CLOSE_SAVED_EDITORS_COMMAND_ID, title: localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20 }); -MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_KEEP_EDITORS_COMMAND_ID, title: localize('togglePreviewMode', "Disable Preview Mode"), toggled: ContextKeyExpr.not('config.workbench.editor.enablePreview') }, group: '7_settings', order: 10 }); +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_KEEP_EDITORS_COMMAND_ID, title: localize('togglePreviewMode', "Enable Preview Editors"), toggled: ContextKeyExpr.has('config.workbench.editor.enablePreview') }, group: '7_settings', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_LOCK_GROUP_COMMAND_ID, title: localize('lockGroup', "Lock Group"), toggled: ActiveEditorGroupLockedContext }, group: '8_lock', order: 10, when: MultipleEditorGroupsContext }); interface IEditorToolItem { id: string; title: string; icon?: { dark?: URI; light?: URI } | ThemeIcon } From 4962bcf90888a9a0c4c8bdb563776df43605ef38 Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 17 Jun 2022 11:09:29 +0200 Subject: [PATCH 135/175] for now disable traffic-light updating --- src/vs/platform/windows/electron-main/window.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index 093408706c7..6fcf7095136 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -281,12 +281,13 @@ export class CodeWindow extends Disposable implements ICodeWindow { if (useCustomTitleStyle && isMacintosh) { const ccConfigKey = 'window.commandCenter'; const trafficLightUpdater = () => { - const on = this.configurationService.getValue(ccConfigKey); - if (on) { - this._win.setTrafficLightPosition({ x: 7, y: 9 }); - } else { - this._win.setTrafficLightPosition({ x: 7, y: 6 }); - } + // temporarily disabled because of https://github.com/microsoft/vscode/pull/150272#issuecomment-1152218493 + // const on = this.configurationService.getValue(ccConfigKey); + // if (on) { + // this._win.setTrafficLightPosition({ x: 7, y: 9 }); + // } else { + // this._win.setTrafficLightPosition({ x: 7, y: 6 }); + // } }; trafficLightUpdater(); this.configurationService.onDidChangeConfiguration(e => { From 59d9fb73bc270a28dcbcd99b5b3412730b8b85d3 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 17 Jun 2022 11:16:33 +0200 Subject: [PATCH 136/175] storage - do not attempt to `close` when path does not exist (#152438) --- src/vs/base/parts/storage/node/storage.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/parts/storage/node/storage.ts b/src/vs/base/parts/storage/node/storage.ts index 789481987b9..f4cf5ac78e1 100644 --- a/src/vs/base/parts/storage/node/storage.ts +++ b/src/vs/base/parts/storage/node/storage.ts @@ -301,9 +301,9 @@ export class SQLiteStorageDatabase implements IStorageDatabase { return new Promise((resolve, reject) => { import('@vscode/sqlite3').then(sqlite3 => { const connection: IDatabaseConnection = { - db: new (this.logger.isTracing ? sqlite3.verbose().Database : sqlite3.Database)(path, error => { + db: new (this.logger.isTracing ? sqlite3.verbose().Database : sqlite3.Database)(path, (error: (Error & { code?: string }) | null) => { if (error) { - return connection.db ? connection.db.close(() => reject(error)) : reject(error); + return (connection.db && error.code !== 'SQLITE_CANTOPEN' /* https://github.com/TryGhost/node-sqlite3/issues/1617 */) ? connection.db.close(() => reject(error)) : reject(error); } // The following exec() statement serves two purposes: From 0662ad271a325d073db25fdc4907352659f39892 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 17 Jun 2022 11:56:58 +0200 Subject: [PATCH 137/175] tests - skip flake (#152145) (#152442) --- .../vscode-api-tests/src/singlefolder-tests/notebook.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts index 270d9ea9aa2..88eb9d8a2bb 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts @@ -197,7 +197,7 @@ const apiTestContentProvider: vscode.NotebookContentProvider = { assert.strictEqual(selectionRedo[0].end, 2); }); - test('editor editing event', async function () { + test.skip('editor editing event', async function () { // TODO@rebornix https://github.com/microsoft/vscode/issues/152145 const notebook = await openRandomNotebookDocument(); const editor = await vscode.window.showNotebookDocument(notebook); From ff7bf7a87548eccb19158ee814e6b8cb1175c4c0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 17 Jun 2022 11:57:27 +0200 Subject: [PATCH 138/175] storage - add a test to assert that invalid path does not hang (#152441) --- .../base/parts/storage/test/node/storage.test.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/vs/base/parts/storage/test/node/storage.test.ts b/src/vs/base/parts/storage/test/node/storage.test.ts index c7652a95df9..f53fdb2c387 100644 --- a/src/vs/base/parts/storage/test/node/storage.test.ts +++ b/src/vs/base/parts/storage/test/node/storage.test.ts @@ -773,4 +773,18 @@ flakySuite('SQLite Storage Library', function () { await storage.close(); }); + + test('invalid path does not hang', async () => { + const storage = new SQLiteStorageDatabase(join(testdir, 'nonexist', 'storage.db')); + + let error; + try { + await storage.getItems(); + await storage.close(); + } catch (e) { + error = e; + } + + ok(error); + }); }); From dd0d79406ef263ce86f7da5dbc1ae5fa6120fe89 Mon Sep 17 00:00:00 2001 From: Robo Date: Fri, 17 Jun 2022 19:04:20 +0900 Subject: [PATCH 139/175] fix: OOM crash with linux smoke tests (#152440) --- test/automation/src/electron.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/automation/src/electron.ts b/test/automation/src/electron.ts index a13e3b1c040..ffde8728787 100644 --- a/test/automation/src/electron.ts +++ b/test/automation/src/electron.ts @@ -42,7 +42,11 @@ export async function resolveElectronConfiguration(options: LaunchOptions): Prom } if (process.platform === 'linux') { - args.push('--disable-gpu'); // Linux has trouble in VMs to render properly with GPU enabled + // --disable-dev-shm-usage: when run on docker containers where size of /dev/shm + // partition < 64MB which causes OOM failure for chromium compositor that uses + // this partition for shared memory. + // Refs https://github.com/microsoft/vscode/issues/152143 + args.push('--disable-dev-shm-usage'); } if (remote) { From 630809a1ea268d7e8f78e91d1143b22b122439d5 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 17 Jun 2022 12:38:36 +0200 Subject: [PATCH 140/175] html: observe insertFinalNewline (#152446) --- .../server/src/cssServer.ts | 5 ++--- extensions/html-language-features/package.json | 6 ------ .../html-language-features/package.nls.json | 1 - .../server/src/modes/htmlMode.ts | 3 +++ .../server/src/test/formatting.test.ts | 16 ++++++---------- 5 files changed, 11 insertions(+), 20 deletions(-) diff --git a/extensions/css-language-features/server/src/cssServer.ts b/extensions/css-language-features/server/src/cssServer.ts index 314aa8aebc0..a22ff9625dd 100644 --- a/extensions/css-language-features/server/src/cssServer.ts +++ b/extensions/css-language-features/server/src/cssServer.ts @@ -7,7 +7,7 @@ import { Connection, TextDocuments, InitializeParams, InitializeResult, ServerCapabilities, ConfigurationRequest, WorkspaceFolder, TextDocumentSyncKind, NotificationType, Disposable, TextDocumentIdentifier, Range, FormattingOptions, TextEdit, Diagnostic } from 'vscode-languageserver'; import { URI } from 'vscode-uri'; -import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet, TextDocument, Position, CSSFormatConfiguration } from 'vscode-css-languageservice'; +import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet, TextDocument, Position } from 'vscode-css-languageservice'; import { getLanguageModelCache } from './languageModelCache'; import { runSafeAsync } from './utils/runner'; import { DiagnosticsSupport, registerDiagnosticsPullSupport, registerDiagnosticsPushSupport } from './utils/validation'; @@ -356,8 +356,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) async function onFormat(textDocument: TextDocumentIdentifier, range: Range | undefined, options: FormattingOptions): Promise { const document = documents.get(textDocument.uri); if (document) { - console.log(JSON.stringify(options)); - const edits = getLanguageService(document).format(document, range ?? getFullRange(document), options as CSSFormatConfiguration); + const edits = getLanguageService(document).format(document, range ?? getFullRange(document), options); if (edits.length > formatterMaxNumberOfEdits) { const newText = TextDocument.applyEdits(document, edits); return [TextEdit.replace(getFullRange(document), newText)]; diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 16a38088f5b..51d53e8984a 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -119,12 +119,6 @@ "default": false, "markdownDescription": "%html.format.indentHandlebars.desc%" }, - "html.format.endWithNewline": { - "type": "boolean", - "scope": "resource", - "default": false, - "description": "%html.format.endWithNewline.desc%" - }, "html.format.extraLiners": { "type": [ "string", diff --git a/extensions/html-language-features/package.nls.json b/extensions/html-language-features/package.nls.json index f5795e33e2e..acb6474d63e 100644 --- a/extensions/html-language-features/package.nls.json +++ b/extensions/html-language-features/package.nls.json @@ -10,7 +10,6 @@ "html.format.preserveNewLines.desc": "Controls whether existing line breaks before elements should be preserved. Only works before elements, not inside tags or for text.", "html.format.maxPreserveNewLines.desc": "Maximum number of line breaks to be preserved in one chunk. Use `null` for unlimited.", "html.format.indentHandlebars.desc": "Format and indent `{{#foo}}` and `{{/foo}}`.", - "html.format.endWithNewline.desc": "End with a newline.", "html.format.extraLiners.desc": "List of tags, comma separated, that should have an extra newline before them. `null` defaults to `\"head, body, /html\"`.", "html.format.wrapAttributes.desc": "Wrap attributes.", "html.format.wrapAttributes.auto": "Wrap attributes only when line length is exceeded.", diff --git a/extensions/html-language-features/server/src/modes/htmlMode.ts b/extensions/html-language-features/server/src/modes/htmlMode.ts index 58a3ded2bee..baca7e0369f 100644 --- a/extensions/html-language-features/server/src/modes/htmlMode.ts +++ b/extensions/html-language-features/server/src/modes/htmlMode.ts @@ -49,6 +49,9 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace: } else { formatSettings.contentUnformatted = 'script'; } + if (formatParams.insertFinalNewline) { + formatSettings.endWithNewline = true; + } merge(formatParams, formatSettings); return htmlLanguageService.format(document, range, formatSettings); }, diff --git a/extensions/html-language-features/server/src/test/formatting.test.ts b/extensions/html-language-features/server/src/test/formatting.test.ts index 775a7b260fd..ebc319d20b6 100644 --- a/extensions/html-language-features/server/src/test/formatting.test.ts +++ b/extensions/html-language-features/server/src/test/formatting.test.ts @@ -85,16 +85,12 @@ suite('HTML Embedded Formatting', () => { }); test('EndWithNewline', async () => { - const options = { - html: { - format: { - endWithNewline: true - } - } - }; - await assertFormat('

    Hello

    ', '\n\n\n

    Hello

    \n\n\n\n', options); - await assertFormat('|

    Hello

    |', '\n

    Hello

    \n', options); - await assertFormat('', '\n\n\n \n\n\n\n', options); + const options : FormattingOptions = FormattingOptions.create(2, true); + options.insertFinalNewline = true; + + await assertFormat('

    Hello

    ', '\n\n\n

    Hello

    \n\n\n\n', {}, options); + await assertFormat('|

    Hello

    |', '\n

    Hello

    \n', {}, options); + await assertFormat('', '\n\n\n \n\n\n\n', {}, options); }); test('Inside script', async () => { From 4e53b01452f2c053ce38c685abd339c57a2ef0f1 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 17 Jun 2022 13:17:09 +0200 Subject: [PATCH 141/175] Pull in cpp grammar fixes (#152449) --- extensions/cpp/cgmanifest.json | 4 +- .../cpp.embedded.macro.tmLanguage.json | 57 +- extensions/cpp/syntaxes/cpp.tmLanguage.json | 115 +- .../test/colorize-results/test-78769_cpp.json | 1024 ++++++++--------- .../test/colorize-results/test_cpp.json | 8 +- 5 files changed, 605 insertions(+), 603 deletions(-) diff --git a/extensions/cpp/cgmanifest.json b/extensions/cpp/cgmanifest.json index d0f49639167..29876c8523b 100644 --- a/extensions/cpp/cgmanifest.json +++ b/extensions/cpp/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "jeff-hykin/better-cpp-syntax", "repositoryUrl": "https://github.com/jeff-hykin/better-cpp-syntax", - "commitHash": "156fc0eef532928c9dbf22f622d4a8efc7d97a6b" + "commitHash": "ddcaa65af8a578881e0d38f3c1cf5259a1128ab5" } }, "license": "MIT", - "version": "1.15.13", + "version": "1.15.17", "description": "The original JSON grammars were derived from https://github.com/atom/language-c which was originally converted from the C TextMate bundle https://github.com/textmate/c.tmbundle." }, { diff --git a/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json b/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json index 7f41320ec40..c85993386c5 100644 --- a/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json +++ b/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/jeff-hykin/better-cpp-syntax/commit/156fc0eef532928c9dbf22f622d4a8efc7d97a6b", + "version": "https://github.com/jeff-hykin/better-cpp-syntax/commit/ddcaa65af8a578881e0d38f3c1cf5259a1128ab5", "name": "C++", "scopeName": "source.cpp.embedded.macro", "patterns": [ @@ -519,7 +519,7 @@ "name": "comment.block.cpp" }, "builtin_storage_type_initilizer": { - "begin": "(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", + "begin": "\\s*+((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\{)", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\{)", "end": "\\}|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))~\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))~\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?((?:error|warning)))\\b(?:(?:\\s)+)?", - "end": "(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?(::))?(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?(::))?(?:(?:\\s)+)?((?|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))|(?=(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?(\\()", + "begin": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?(\\()", "end": "\\)|(?=(?))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\()", + "begin": "(?:(?:^|\\G|(?<=;|\\}))|(?<=>))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\()", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)?(?![\\w<:.]))", + "match": "(?<=^|\\))(?:(?:\\s)+)?(->)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "punctuation.definition.function.return-type.cpp" @@ -4240,7 +4240,7 @@ ] }, "function_pointer": { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()|(?=(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))", + "match": "(?<=protected|virtual|private|public|,|:)(?:(?:\\s)+)?(?!(?:(?:(?:protected)|(?:private)|(?:public))|virtual))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "meta.qualified_type.cpp", @@ -5368,7 +5368,7 @@ }, "line": { "begin": "^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?line\\b", - "end": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?define\\b)(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<4>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<4>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(operator)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(?:(?:((?:(?:delete\\[\\])|(?:delete)|(?:new\\[\\])|(?:new)|(?:\\->\\*)|(?:<<=)|(?:>>=)|(?:<=>)|(?:\\+\\+)|(?:\\-\\-)|(?:\\(\\))|(?:\\[\\])|(?:\\->)|(?:\\+\\+)|(?:\\-\\-)|(?:<<)|(?:>>)|(?:<=)|(?:>=)|(?:==)|(?:!=)|(?:&&)|(?:\\|\\|)|(?:\\+=)|(?:\\-=)|(?:\\*=)|(?:\\/=)|(?:%=)|(?:&=)|(?:\\^=)|(?:\\|=)|(?:\\+)|(?:\\-)|!|~|(?:\\*)|&|(?:\\*)|(?:\\/)|%|(?:\\+)|(?:\\-)|<|>|&|(?:\\^)|(?:\\|)|=|,))|((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:\\[\\])?)))|(\"\")((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\<|\\()", + "begin": "(?:(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(operator)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(?:(?:((?:(?:delete\\[\\])|(?:delete)|(?:new\\[\\])|(?:<=>)|(?:<<=)|(?:new)|(?:>>=)|(?:\\->\\*)|(?:\\/=)|(?:%=)|(?:&=)|(?:>=)|(?:\\|=)|(?:\\+\\+)|(?:\\-\\-)|(?:\\(\\))|(?:\\[\\])|(?:\\->)|(?:\\+\\+)|(?:<<)|(?:>>)|(?:\\-\\-)|(?:<=)|(?:\\^=)|(?:==)|(?:!=)|(?:&&)|(?:\\|\\|)|(?:\\+=)|(?:\\-=)|(?:\\*=)|,|(?:\\+)|(?:\\-)|!|~|(?:\\*)|&|(?:\\*)|(?:\\/)|%|(?:\\+)|(?:\\-)|<|>|&|(?:\\^)|(?:\\|)|=))|((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:\\[\\])?)))|(\"\")((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\<|\\()", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", + "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -7350,7 +7351,7 @@ "include": "source.cpp#vararg_ellipses" }, { - "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", + "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -7702,7 +7703,7 @@ }, "pragma": { "begin": "^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?pragma\\b", - "end": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()|(?=(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<6>?)+>)(?:\\s)*+)?::)*\\s*+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<6>?)+>)(?:\\s)*+)?::)*\\s*+)?((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", + "begin": "\\s*+((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { "0": { @@ -2176,7 +2176,7 @@ ] }, "control_flow_keywords": { - "match": "((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\{)", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\{)", "end": "\\}", "beginCaptures": { "1": { @@ -3363,7 +3363,7 @@ ] }, "destructor_root": { - "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))~\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))~\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { "0": { @@ -3655,7 +3655,7 @@ }, "diagnostic": { "begin": "(^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?((?:error|warning)))\\b(?:(?:\\s)+)?", - "end": "(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?(::))?(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?(::))?(?:(?:\\s)+)?((?|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))", "beginCaptures": { "0": { @@ -4519,7 +4519,7 @@ ] }, "function_call": { - "begin": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?(\\()", + "begin": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?(\\()", "end": "\\)", "beginCaptures": { "1": { @@ -4593,7 +4593,7 @@ ] }, "function_definition": { - "begin": "(?:(?:^|\\G|(?<=;|\\}))|(?<=>))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\()", + "begin": "(?:(?:^|\\G|(?<=;|\\}))|(?<=>))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\()", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { "0": { @@ -5156,7 +5156,7 @@ ] }, { - "match": "(?<=^|\\))(?:(?:\\s)+)?(->)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)?(?![\\w<:.]))", + "match": "(?<=^|\\))(?:(?:\\s)+)?(->)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "punctuation.definition.function.return-type.cpp" @@ -5404,7 +5404,7 @@ ] }, "function_pointer": { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()", "beginCaptures": { "1": { @@ -5755,7 +5755,7 @@ ] }, "function_pointer_parameter": { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()", "beginCaptures": { "1": { @@ -6476,7 +6476,7 @@ "name": "storage.type.modifier.virtual.cpp" }, { - "match": "(?<=protected|virtual|private|public|,|:)(?:(?:\\s)+)?(?!(?:(?:(?:protected)|(?:private)|(?:public))|virtual))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))", + "match": "(?<=protected|virtual|private|public|,|:)(?:(?:\\s)+)?(?!(?:(?:(?:protected)|(?:private)|(?:public))|virtual))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "meta.qualified_type.cpp", @@ -6674,7 +6674,7 @@ ] }, "inline_builtin_storage_type": { - "match": "(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?line\\b", - "end": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?define\\b)(?:(?:\\s)+)?((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:(?:\\s)+)?(?:(?:\\.\\*|\\.)|(?:->\\*|->))(?:(?:\\s)+)?)*)(?:(?:\\s)+)?(\\b(?!uint_least16_t[^\\w]|uint_least32_t[^\\w]|uint_least64_t[^\\w]|int_least16_t[^\\w]|int_least32_t[^\\w]|int_least64_t[^\\w]|uint_least8_t[^\\w]|uint_fast16_t[^\\w]|uint_fast32_t[^\\w]|uint_fast64_t[^\\w]|int_least8_t[^\\w]|int_fast16_t[^\\w]|int_fast32_t[^\\w]|int_fast64_t[^\\w]|uint_fast8_t[^\\w]|suseconds_t[^\\w]|int_fast8_t[^\\w]|useconds_t[^\\w]|blksize_t[^\\w]|in_addr_t[^\\w]|in_port_t[^\\w]|uintptr_t[^\\w]|uintmax_t[^\\w]|uintmax_t[^\\w]|uintmax_t[^\\w]|unsigned[^\\w]|u_quad_t[^\\w]|blkcnt_t[^\\w]|uint16_t[^\\w]|uint32_t[^\\w]|uint64_t[^\\w]|intptr_t[^\\w]|intmax_t[^\\w]|intmax_t[^\\w]|wchar_t[^\\w]|u_short[^\\w]|qaddr_t[^\\w]|caddr_t[^\\w]|daddr_t[^\\w]|fixpt_t[^\\w]|nlink_t[^\\w]|segsz_t[^\\w]|swblk_t[^\\w]|clock_t[^\\w]|ssize_t[^\\w]|int16_t[^\\w]|int32_t[^\\w]|int64_t[^\\w]|uint8_t[^\\w]|signed[^\\w]|double[^\\w]|u_char[^\\w]|u_long[^\\w]|ushort[^\\w]|quad_t[^\\w]|mode_t[^\\w]|size_t[^\\w]|time_t[^\\w]|int8_t[^\\w]|short[^\\w]|float[^\\w]|u_int[^\\w]|div_t[^\\w]|dev_t[^\\w]|gid_t[^\\w]|ino_t[^\\w]|key_t[^\\w]|pid_t[^\\w]|off_t[^\\w]|uid_t[^\\w]|auto[^\\w]|void[^\\w]|char[^\\w]|long[^\\w]|bool[^\\w]|uint[^\\w]|id_t[^\\w]|id_t[^\\w]|int[^\\w])(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b(?!\\())", + "match": "(?:((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:(?:\\s)+)?(?:(?:\\.\\*|\\.)|(?:->\\*|->))(?:(?:\\s)+)?)*)(?:(?:\\s)+)?(\\b(?!uint_least32_t[^\\w]|uint_least16_t[^\\w]|uint_least64_t[^\\w]|int_least32_t[^\\w]|int_least64_t[^\\w]|uint_fast32_t[^\\w]|uint_fast64_t[^\\w]|uint_least8_t[^\\w]|uint_fast16_t[^\\w]|int_least16_t[^\\w]|int_fast16_t[^\\w]|int_least8_t[^\\w]|uint_fast8_t[^\\w]|int_fast64_t[^\\w]|int_fast32_t[^\\w]|int_fast8_t[^\\w]|suseconds_t[^\\w]|useconds_t[^\\w]|in_addr_t[^\\w]|uintmax_t[^\\w]|uintmax_t[^\\w]|uintmax_t[^\\w]|in_port_t[^\\w]|uintptr_t[^\\w]|blksize_t[^\\w]|uint32_t[^\\w]|uint64_t[^\\w]|u_quad_t[^\\w]|intmax_t[^\\w]|intmax_t[^\\w]|unsigned[^\\w]|blkcnt_t[^\\w]|uint16_t[^\\w]|intptr_t[^\\w]|swblk_t[^\\w]|wchar_t[^\\w]|u_short[^\\w]|qaddr_t[^\\w]|caddr_t[^\\w]|daddr_t[^\\w]|fixpt_t[^\\w]|nlink_t[^\\w]|segsz_t[^\\w]|clock_t[^\\w]|ssize_t[^\\w]|int16_t[^\\w]|int32_t[^\\w]|int64_t[^\\w]|uint8_t[^\\w]|int8_t[^\\w]|mode_t[^\\w]|quad_t[^\\w]|ushort[^\\w]|u_long[^\\w]|u_char[^\\w]|double[^\\w]|signed[^\\w]|time_t[^\\w]|size_t[^\\w]|key_t[^\\w]|div_t[^\\w]|ino_t[^\\w]|uid_t[^\\w]|gid_t[^\\w]|off_t[^\\w]|pid_t[^\\w]|float[^\\w]|dev_t[^\\w]|u_int[^\\w]|short[^\\w]|bool[^\\w]|id_t[^\\w]|uint[^\\w]|long[^\\w]|char[^\\w]|void[^\\w]|auto[^\\w]|id_t[^\\w]|int[^\\w])(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b(?!\\())", "captures": { "1": { "patterns": [ @@ -7574,7 +7575,7 @@ ] }, "namespace_alias": { - "match": "(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<8>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<8>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<4>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<4>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(operator)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(?:(?:((?:(?:delete\\[\\])|(?:delete)|(?:new\\[\\])|(?:new)|(?:\\->\\*)|(?:<<=)|(?:>>=)|(?:<=>)|(?:\\+\\+)|(?:\\-\\-)|(?:\\(\\))|(?:\\[\\])|(?:\\->)|(?:\\+\\+)|(?:\\-\\-)|(?:<<)|(?:>>)|(?:<=)|(?:>=)|(?:==)|(?:!=)|(?:&&)|(?:\\|\\|)|(?:\\+=)|(?:\\-=)|(?:\\*=)|(?:\\/=)|(?:%=)|(?:&=)|(?:\\^=)|(?:\\|=)|(?:\\+)|(?:\\-)|!|~|(?:\\*)|&|(?:\\*)|(?:\\/)|%|(?:\\+)|(?:\\-)|<|>|&|(?:\\^)|(?:\\|)|=|,))|((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:\\[\\])?)))|(\"\")((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\<|\\()", + "begin": "(?:(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(operator)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(?:(?:((?:(?:delete\\[\\])|(?:delete)|(?:new\\[\\])|(?:<=>)|(?:<<=)|(?:new)|(?:>>=)|(?:\\->\\*)|(?:\\/=)|(?:%=)|(?:&=)|(?:>=)|(?:\\|=)|(?:\\+\\+)|(?:\\-\\-)|(?:\\(\\))|(?:\\[\\])|(?:\\->)|(?:\\+\\+)|(?:<<)|(?:>>)|(?:\\-\\-)|(?:<=)|(?:\\^=)|(?:==)|(?:!=)|(?:&&)|(?:\\|\\|)|(?:\\+=)|(?:\\-=)|(?:\\*=)|,|(?:\\+)|(?:\\-)|!|~|(?:\\*)|&|(?:\\*)|(?:\\/)|%|(?:\\+)|(?:\\-)|<|>|&|(?:\\^)|(?:\\|)|=))|((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:\\[\\])?)))|(\"\")((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\<|\\()", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { "0": { @@ -10440,7 +10441,7 @@ "include": "#vararg_ellipses" }, { - "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", + "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -11478,7 +11479,7 @@ "include": "#vararg_ellipses" }, { - "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", + "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -12488,7 +12489,7 @@ }, "pragma": { "begin": "^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?pragma\\b", - "end": "(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)?(?![\\w<:.])", + "match": "\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)?(?![\\w<:.])", "captures": { "0": { "patterns": [ @@ -13083,7 +13084,7 @@ } }, "scope_resolution": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13105,7 +13106,7 @@ } }, "scope_resolution_function_call": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13127,7 +13128,7 @@ } }, "scope_resolution_function_call_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13165,7 +13166,7 @@ } }, "scope_resolution_function_definition": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13187,7 +13188,7 @@ } }, "scope_resolution_function_definition_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13225,7 +13226,7 @@ } }, "scope_resolution_function_definition_operator_overload": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13247,7 +13248,7 @@ } }, "scope_resolution_function_definition_operator_overload_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13285,7 +13286,7 @@ } }, "scope_resolution_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13323,7 +13324,7 @@ } }, "scope_resolution_namespace_alias": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13345,7 +13346,7 @@ } }, "scope_resolution_namespace_alias_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13383,7 +13384,7 @@ } }, "scope_resolution_namespace_block": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13405,7 +13406,7 @@ } }, "scope_resolution_namespace_block_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13443,7 +13444,7 @@ } }, "scope_resolution_namespace_using": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13465,7 +13466,7 @@ } }, "scope_resolution_namespace_using_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13503,7 +13504,7 @@ } }, "scope_resolution_parameter": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13525,7 +13526,7 @@ } }, "scope_resolution_parameter_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13563,7 +13564,7 @@ } }, "scope_resolution_template_call": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13585,7 +13586,7 @@ } }, "scope_resolution_template_call_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13623,7 +13624,7 @@ } }, "scope_resolution_template_definition": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13645,7 +13646,7 @@ } }, "scope_resolution_template_definition_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13687,7 +13688,7 @@ "name": "punctuation.terminator.statement.cpp" }, "simple_type": { - "match": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?", + "match": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?", "captures": { "1": { "name": "meta.qualified_type.cpp", @@ -16491,7 +16492,7 @@ } }, "type_alias": { - "match": "(using)(?:(?:\\s)+)?(?!namespace)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)?(?![\\w<:.]))(?:(?:\\s)+)?(\\=)(?:(?:\\s)+)?((?:typename)?)(?:(?:\\s)+)?((?:(?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)?(?![\\w<:.]))|(.*(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)?(?:(?:\\s)+)?(?:(;)|\\n)", + "match": "(using)(?:(?:\\s)+)?(?!namespace)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)?(?![\\w<:.]))(?:(?:\\s)+)?(\\=)(?:(?:\\s)+)?((?:typename)?)(?:(?:\\s)+)?((?:(?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)?(?![\\w<:.]))|(.*(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)?(?:(?:\\s)+)?(?:(;)|\\n)", "captures": { "1": { "name": "keyword.other.using.directive.cpp" @@ -17581,7 +17582,7 @@ "endCaptures": {}, "patterns": [ { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()", "beginCaptures": { "1": { @@ -18866,7 +18867,7 @@ ] }, "typename": { - "match": "(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<17>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<17>?)+>)?(?![\\w<:.]))", + "match": "(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<17>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<17>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "storage.modifier.cpp" @@ -19755,7 +19756,7 @@ } }, "using_namespace": { - "begin": "(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<6>?)+>)(?:\\s)*+)?::)*\\s*+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<6>?)+>)(?:\\s)*+)?::)*\\s*+)?((? Date: Fri, 17 Jun 2022 04:21:15 -0700 Subject: [PATCH 142/175] Try aligning tree view drop handling (#150468) * Try aligning external drop handling Tree views provided the first public drag and drop api. We've since also adopted drag and drop for dropping into editors as well, which resulted in some duplicated code between it and the tree view implementation This PR tried to better align the two by: - Use `extractEditorsDropData` to add the uriList - In the tree view, use `toVSDataTransfer` to convert the event to a `VSDataTransfer` - Remove `convertKnownMimes` as `extractEditorsDropData` handles this now * Add uriList for drag * Also handle 'DataTransfers.RESOURCES' * Fix monaco --- src/vs/editor/browser/dnd.ts | 36 ++++++- .../browser/dropIntoEditorContribution.ts | 18 +--- src/vs/platform/dnd/browser/dnd.ts | 32 +++--- src/vs/workbench/browser/dnd.ts | 4 +- .../workbench/browser/parts/views/treeView.ts | 99 +++++++------------ .../extensions/browser/extensionsViewlet.ts | 4 +- .../contrib/files/browser/fileImportExport.ts | 4 +- 7 files changed, 98 insertions(+), 99 deletions(-) diff --git a/src/vs/editor/browser/dnd.ts b/src/vs/editor/browser/dnd.ts index 80a5958504d..b90bf922695 100644 --- a/src/vs/editor/browser/dnd.ts +++ b/src/vs/editor/browser/dnd.ts @@ -3,9 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { DataTransfers } from 'vs/base/browser/dnd'; +import { distinct } from 'vs/base/common/arrays'; import { createFileDataTransferItem, createStringDataTransferItem, IDataTransferItem, VSDataTransfer } from 'vs/base/common/dataTransfer'; +import { Mimes } from 'vs/base/common/mime'; import { URI } from 'vs/base/common/uri'; -import { FileAdditionalNativeProperties } from 'vs/platform/dnd/browser/dnd'; +import { CodeDataTransfers, extractEditorsDropData, FileAdditionalNativeProperties } from 'vs/platform/dnd/browser/dnd'; export function toVSDataTransfer(dataTransfer: DataTransfer) { @@ -31,3 +34,34 @@ export function createFileDataTransferItemFromFile(file: File): IDataTransferIte return new Uint8Array(await file.arrayBuffer()); }); } + +const INTERNAL_DND_MIME_TYPES = Object.freeze([ + CodeDataTransfers.EDITORS, + CodeDataTransfers.FILES, + DataTransfers.RESOURCES, +]); + +export function addExternalEditorsDropData(dataTransfer: VSDataTransfer, dragEvent: DragEvent) { + if (dragEvent.dataTransfer && !dataTransfer.has(Mimes.uriList)) { + const editorData = extractEditorsDropData(dragEvent) + .filter(input => input.resource) + .map(input => input.resource!.toString()); + + // Also add in the files + for (const item of dragEvent.dataTransfer?.items) { + const file = item.getAsFile(); + if (file) { + editorData.push((file as FileAdditionalNativeProperties).path ? URI.file((file as FileAdditionalNativeProperties).path!).toString() : file.name); + } + } + + if (editorData.length) { + const str = distinct(editorData).join('\n'); + dataTransfer.replace(Mimes.uriList, createStringDataTransferItem(str)); + } + } + + for (const internal of INTERNAL_DND_MIME_TYPES) { + dataTransfer.delete(internal); + } +} diff --git a/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts b/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts index 8254c5faf12..0c050ea25f6 100644 --- a/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts +++ b/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts @@ -3,14 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { distinct } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { createStringDataTransferItem, VSDataTransfer } from 'vs/base/common/dataTransfer'; +import { VSDataTransfer } from 'vs/base/common/dataTransfer'; import { Disposable } from 'vs/base/common/lifecycle'; import { Mimes } from 'vs/base/common/mime'; import { relativePath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { toVSDataTransfer } from 'vs/editor/browser/dnd'; +import { addExternalEditorsDropData, toVSDataTransfer } from 'vs/editor/browser/dnd'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { IBulkEditService, ResourceEdit } from 'vs/editor/browser/services/bulkEditService'; @@ -25,8 +24,6 @@ import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from 'vs/edit import { performSnippetEdit } from 'vs/editor/contrib/snippet/browser/snippetController2'; import { SnippetParser } from 'vs/editor/contrib/snippet/browser/snippetParser'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { extractEditorsDropData } from 'vs/platform/dnd/browser/dnd'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -37,7 +34,6 @@ export class DropIntoEditorController extends Disposable implements IEditorContr constructor( editor: ICodeEditor, @IWorkspaceContextService workspaceContextService: IWorkspaceContextService, - @IInstantiationService private readonly _instantiationService: IInstantiationService, @ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IBulkEditService private readonly _bulkEditService: IBulkEditService, @@ -111,15 +107,7 @@ export class DropIntoEditorController extends Disposable implements IEditorContr } const textEditorDataTransfer = toVSDataTransfer(dragEvent.dataTransfer); - const editorData = (await this._instantiationService.invokeFunction(extractEditorsDropData, dragEvent)) - .filter(input => input.resource) - .map(input => input.resource!.toString()); - - if (editorData.length) { - const str = distinct(editorData).join('\n'); - textEditorDataTransfer.replace(Mimes.uriList, createStringDataTransferItem(str)); - } - + addExternalEditorsDropData(textEditorDataTransfer, dragEvent); return textEditorDataTransfer; } } diff --git a/src/vs/platform/dnd/browser/dnd.ts b/src/vs/platform/dnd/browser/dnd.ts index 14c6c8d11d3..4fafc45e2ec 100644 --- a/src/vs/platform/dnd/browser/dnd.ts +++ b/src/vs/platform/dnd/browser/dnd.ts @@ -56,7 +56,7 @@ export interface IDraggedResourceEditorInput extends IBaseTextResourceEditorInpu allowWorkspaceOpen?: boolean; } -export async function extractEditorsDropData(accessor: ServicesAccessor, e: DragEvent): Promise> { +export function extractEditorsDropData(e: DragEvent): Array { const editors: IDraggedResourceEditorInput[] = []; if (e.dataTransfer && e.dataTransfer.types.length > 0) { @@ -107,18 +107,6 @@ export async function extractEditorsDropData(accessor: ServicesAccessor, e: Drag } } - // Web: Check for file transfer - if (isWeb && containsDragType(e, DataTransfers.FILES)) { - const files = e.dataTransfer.items; - if (files) { - const instantiationService = accessor.get(IInstantiationService); - const filesData = await instantiationService.invokeFunction(accessor => extractFilesDropData(accessor, e)); - for (const fileData of filesData) { - editors.push({ resource: fileData.resource, contents: fileData.contents?.toString(), isExternal: true, allowWorkspaceOpen: fileData.isDirectory }); - } - } - } - // Workbench contributions const contributions = Registry.as(Extensions.DragAndDropContribution).getAll(); for (const contribution of contributions) { @@ -136,6 +124,24 @@ export async function extractEditorsDropData(accessor: ServicesAccessor, e: Drag return editors; } +export async function extractEditorsAndFilesDropData(accessor: ServicesAccessor, e: DragEvent): Promise> { + const editors = extractEditorsDropData(e); + + // Web: Check for file transfer + if (e.dataTransfer && isWeb && containsDragType(e, DataTransfers.FILES)) { + const files = e.dataTransfer.items; + if (files) { + const instantiationService = accessor.get(IInstantiationService); + const filesData = await instantiationService.invokeFunction(accessor => extractFilesDropData(accessor, e)); + for (const fileData of filesData) { + editors.push({ resource: fileData.resource, contents: fileData.contents?.toString(), isExternal: true, allowWorkspaceOpen: fileData.isDirectory }); + } + } + } + + return editors; +} + export function createDraggedEditorInputFromRawResourcesData(rawResourcesData: string | undefined): IDraggedResourceEditorInput[] { const editors: IDraggedResourceEditorInput[] = []; diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index 3a186e54ab4..dc877136e37 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -19,7 +19,7 @@ import { FileAccess, Schemas } from 'vs/base/common/network'; import { isWindows } from 'vs/base/common/platform'; import { basename, isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { CodeDataTransfers, createDraggedEditorInputFromRawResourcesData, Extensions, extractEditorsDropData, IDragAndDropContributionRegistry, IDraggedResourceEditorInput, IResourceStat } from 'vs/platform/dnd/browser/dnd'; +import { CodeDataTransfers, createDraggedEditorInputFromRawResourcesData, Extensions, extractEditorsAndFilesDropData, IDragAndDropContributionRegistry, IDraggedResourceEditorInput, IResourceStat } from 'vs/platform/dnd/browser/dnd'; import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -105,7 +105,7 @@ export class ResourcesDropHandler { } async handleDrop(event: DragEvent, resolveTargetGroup: () => IEditorGroup | undefined, afterDrop: (targetGroup: IEditorGroup | undefined) => void, targetIndex?: number): Promise { - const editors = await this.instantiationService.invokeFunction(accessor => extractEditorsDropData(accessor, event)); + const editors = await this.instantiationService.invokeFunction(accessor => extractEditorsAndFilesDropData(accessor, event)); if (!editors.length) { return; } diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 32078d1bf2b..fefe8a7eade 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -31,7 +31,7 @@ import { isString } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import 'vs/css!./media/views'; -import { createStringDataTransferItem, VSDataTransfer } from 'vs/base/common/dataTransfer'; +import { VSDataTransfer } from 'vs/base/common/dataTransfer'; import { Command } from 'vs/editor/common/languages'; import { localize } from 'vs/nls'; import { createActionViewItem, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; @@ -54,7 +54,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { focusBorder, listFilterMatchHighlight, listFilterMatchHighlightBorder, textCodeBlockBackground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; import { ColorScheme } from 'vs/platform/theme/common/theme'; import { FileThemeIcon, FolderThemeIcon, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; -import { convertResourceUrlsToUriList, DraggedTreeItemsIdentifier, fillEditorsDragData, LocalSelectionTransfer } from 'vs/workbench/browser/dnd'; +import { DraggedTreeItemsIdentifier, fillEditorsDragData, LocalSelectionTransfer } from 'vs/workbench/browser/dnd'; import { IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels'; import { API_OPEN_DIFF_EDITOR_COMMAND_ID, API_OPEN_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; @@ -66,7 +66,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; import { ITreeViewsService } from 'vs/workbench/services/views/browser/treeViewsService'; import { CodeDataTransfers } from 'vs/platform/dnd/browser/dnd'; -import { createFileDataTransferItemFromFile } from 'vs/editor/browser/dnd'; +import { addExternalEditorsDropData, toVSDataTransfer } from 'vs/editor/browser/dnd'; export class TreeViewPane extends ViewPane { @@ -1332,8 +1332,6 @@ interface TreeDragSourceInfo { itemHandles: string[]; } -const INTERNAL_MIME_TYPES = [CodeDataTransfers.EDITORS.toLowerCase(), CodeDataTransfers.FILES.toLowerCase()]; - export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { private readonly treeMimeType: string; private readonly treeItemsTransfer = LocalSelectionTransfer.getInstance(); @@ -1432,14 +1430,23 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { } onDragOver(data: IDragAndDropData, targetElement: ITreeItem, targetIndex: number, originalEvent: DragEvent): boolean | ITreeDragOverReaction { - const types: Set = new Set(); - originalEvent.dataTransfer?.types.forEach((value, index) => { - if (INTERNAL_MIME_TYPES.indexOf(value) < 0) { - types.add(this.convertKnownMimes(value).type); + const dataTransfer = toVSDataTransfer(originalEvent.dataTransfer!); + addExternalEditorsDropData(dataTransfer, originalEvent); + + const types = new Set(Array.from(dataTransfer.entries()).map(x => x[0])); + + if (originalEvent.dataTransfer) { + // Also add uri-list if we have any files. At this stage we can't actually access the file itself though. + for (const item of originalEvent.dataTransfer.items) { + if (item.kind === 'file' || item.type === DataTransfers.RESOURCES.toLowerCase()) { + types.add(Mimes.uriList); + break; + } } - }); + } this.debugLog(types); + const dndController = this.dndController; if (!dndController || !originalEvent.dataTransfer || (dndController.dropMimeTypes.length === 0)) { return false; @@ -1475,26 +1482,11 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { return element.label ? element.label.label : (element.resourceUri ? this.labelService.getUriLabel(URI.revive(element.resourceUri)) : undefined); } - private convertKnownMimes(type: string, kind?: string, value?: string | FileSystemHandle): { type: string; value?: string } { - let convertedValue = undefined; - let convertedType = type; - if (type === DataTransfers.RESOURCES.toLowerCase()) { - convertedValue = value ? convertResourceUrlsToUriList(value as string) : undefined; - convertedType = Mimes.uriList; - } else if ((type === 'Files') || (kind === 'file')) { - convertedType = Mimes.uriList; - convertedValue = value ? (value as FileSystemHandle).name : undefined; - } - return { type: convertedType, value: convertedValue }; - } - async drop(data: IDragAndDropData, targetNode: ITreeItem | undefined, targetIndex: number | undefined, originalEvent: DragEvent): Promise { const dndController = this.dndController; if (!originalEvent.dataTransfer || !dndController) { return; } - const treeDataTransfer = new VSDataTransfer(); - const uris: URI[] = []; let treeSourceInfo: TreeDragSourceInfo | undefined; let willDropUuid: string | undefined; @@ -1502,51 +1494,30 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { willDropUuid = this.treeItemsTransfer.getData(DraggedTreeItemsIdentifier.prototype)![0].identifier; } - await Promise.all([...originalEvent.dataTransfer.items].map(async dataItem => { - const type = dataItem.type; - const kind = dataItem.kind; - const convertedType = this.convertKnownMimes(type, kind).type; - if ((INTERNAL_MIME_TYPES.indexOf(convertedType) < 0) - && (convertedType === this.treeMimeType) || (dndController.dropMimeTypes.indexOf(convertedType) >= 0)) { - if (dataItem.kind === 'string') { - await new Promise(resolve => - dataItem.getAsString(dataValue => { - if (convertedType === this.treeMimeType) { - treeSourceInfo = JSON.parse(dataValue); - } - if (dataValue) { - const converted = this.convertKnownMimes(type, kind, dataValue); - treeDataTransfer.append(converted.type, createStringDataTransferItem(converted.value + '')); - } - resolve(); - })); - } else if (dataItem.kind === 'file') { - const file = dataItem.getAsFile(); - if (file) { - uris.push(URI.file(file.path)); - treeDataTransfer.append(type, createFileDataTransferItemFromFile(file)); + const originalDataTransfer = toVSDataTransfer(originalEvent.dataTransfer); + addExternalEditorsDropData(originalDataTransfer, originalEvent); + + const outDataTransfer = new VSDataTransfer(); + for (const [type, item] of originalDataTransfer.entries()) { + if (type === this.treeMimeType || dndController.dropMimeTypes.indexOf(type) >= 0) { + outDataTransfer.append(type, item); + if (type === this.treeMimeType) { + try { + treeSourceInfo = JSON.parse(await item.asString()); + } catch { + // noop } } } - })); - - // Check if there are uris to add and add them - if (uris.length) { - treeDataTransfer.replace(Mimes.uriList, createStringDataTransferItem(uris.map(uri => uri.toString()).join('\n'))); } - const additionalWillDropPromise = this.treeViewsDragAndDropService.removeDragOperationTransfer(willDropUuid); - if (!additionalWillDropPromise) { - return dndController.handleDrop(treeDataTransfer, targetNode, new CancellationTokenSource().token, willDropUuid, treeSourceInfo?.id, treeSourceInfo?.itemHandles); - } - return additionalWillDropPromise.then(additionalDataTransfer => { - if (additionalDataTransfer) { - for (const item of additionalDataTransfer.entries()) { - treeDataTransfer.append(item[0], item[1]); - } + const additionalDataTransfer = await this.treeViewsDragAndDropService.removeDragOperationTransfer(willDropUuid); + if (additionalDataTransfer) { + for (const item of additionalDataTransfer.entries()) { + outDataTransfer.append(item[0], item[1]); } - return dndController.handleDrop(treeDataTransfer, targetNode, new CancellationTokenSource().token, willDropUuid, treeSourceInfo?.id, treeSourceInfo?.itemHandles); - }); + } + return dndController.handleDrop(outDataTransfer, targetNode, new CancellationTokenSource().token, willDropUuid, treeSourceInfo?.id, treeSourceInfo?.itemHandles); } onDragEnd(originalEvent: DragEvent): void { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index aaf033d7bd5..ae5a743a290 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -58,7 +58,7 @@ import { registerAction2, Action2, MenuId } from 'vs/platform/actions/common/act import { IPaneComposite } from 'vs/workbench/common/panecomposite'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { coalesce } from 'vs/base/common/arrays'; -import { extractEditorsDropData } from 'vs/platform/dnd/browser/dnd'; +import { extractEditorsAndFilesDropData } from 'vs/platform/dnd/browser/dnd'; import { extname } from 'vs/base/common/resources'; const SearchMarketplaceExtensionsContext = new RawContextKey('searchMarketplaceExtensions', false); @@ -541,7 +541,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE if (this.isSupportedDragElement(e)) { hide(overlay); - const vsixs = coalesce((await this.instantiationService.invokeFunction(accessor => extractEditorsDropData(accessor, e))) + const vsixs = coalesce((await this.instantiationService.invokeFunction(accessor => extractEditorsAndFilesDropData(accessor, e))) .map(editor => editor.resource && extname(editor.resource) === '.vsix' ? editor.resource : undefined)); if (vsixs.length > 0) { diff --git a/src/vs/workbench/contrib/files/browser/fileImportExport.ts b/src/vs/workbench/contrib/files/browser/fileImportExport.ts index 2215ecd93ad..647334b791c 100644 --- a/src/vs/workbench/contrib/files/browser/fileImportExport.ts +++ b/src/vs/workbench/contrib/files/browser/fileImportExport.ts @@ -20,7 +20,7 @@ import { ExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel'; import { URI } from 'vs/base/common/uri'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { extractEditorsDropData } from 'vs/platform/dnd/browser/dnd'; +import { extractEditorsAndFilesDropData } from 'vs/platform/dnd/browser/dnd'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { isWeb } from 'vs/base/common/platform'; import { triggerDownload } from 'vs/base/browser/dom'; @@ -427,7 +427,7 @@ export class ExternalFileImport { private async doImport(target: ExplorerItem, source: DragEvent, token: CancellationToken): Promise { // Activate all providers for the resources dropped - const candidateFiles = coalesce((await this.instantiationService.invokeFunction(accessor => extractEditorsDropData(accessor, source))).map(editor => editor.resource)); + const candidateFiles = coalesce((await this.instantiationService.invokeFunction(accessor => extractEditorsAndFilesDropData(accessor, source))).map(editor => editor.resource)); await Promise.all(candidateFiles.map(resource => this.fileService.activateProvider(resource.scheme))); // Check for dropped external files to be folders From 5ff4901e69b7b76666ff5f4368632dc2bf9a03ca Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 17 Jun 2022 14:12:50 +0200 Subject: [PATCH 143/175] joh/issue150907 (#152447) * Use variants of the foreground color as CC border * use panel border color for CC border default --- .../browser/parts/titlebar/commandCenterControl.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts b/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts index fc9f14b7ea8..dd0207b43a2 100644 --- a/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts @@ -24,7 +24,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import * as colors from 'vs/platform/theme/common/colorRegistry'; import { WindowTitle } from 'vs/workbench/browser/parts/titlebar/windowTitle'; -import { MENUBAR_SELECTION_BACKGROUND, MENUBAR_SELECTION_FOREGROUND, TITLE_BAR_ACTIVE_FOREGROUND } from 'vs/workbench/common/theme'; +import { MENUBAR_SELECTION_BACKGROUND, MENUBAR_SELECTION_FOREGROUND, PANEL_BORDER, TITLE_BAR_ACTIVE_FOREGROUND } from 'vs/workbench/common/theme'; import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; export class CommandCenterControl { @@ -186,7 +186,7 @@ colors.registerColor( localize('commandCenter-background', "Background color of the command center"), false ); -const activeBackground = colors.registerColor( +colors.registerColor( 'commandCenter.activeBackground', { dark: MENUBAR_SELECTION_BACKGROUND, hcDark: MENUBAR_SELECTION_BACKGROUND, light: MENUBAR_SELECTION_BACKGROUND, hcLight: MENUBAR_SELECTION_BACKGROUND }, localize('commandCenter-activeBackground', "Active background color of the command center"), @@ -194,8 +194,7 @@ const activeBackground = colors.registerColor( ); // border: defaults to active background colors.registerColor( - 'commandCenter.border', - { dark: activeBackground, hcDark: colors.inputBorder, light: activeBackground, hcLight: colors.inputBorder }, + 'commandCenter.border', { dark: PANEL_BORDER, hcDark: PANEL_BORDER, light: PANEL_BORDER, hcLight: PANEL_BORDER }, localize('commandCenter-border', "Border color of the command center"), false ); From d20d9d6558788be53a970914323a9dac12083336 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 17 Jun 2022 14:34:11 +0200 Subject: [PATCH 144/175] add `cross-origin-isolated` to web-worker ext host iframe (#152462) add `cross-origin-isolated` to web-worker ext host iframe, related https://github.com/microsoft/vscode/issues/137884 --- .../services/extensions/browser/webWorkerExtensionHost.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts index f387703969b..ec07d5de4b4 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts @@ -132,7 +132,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost const iframe = document.createElement('iframe'); iframe.setAttribute('class', 'web-worker-ext-host-iframe'); iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin'); - iframe.setAttribute('allow', 'usb'); + iframe.setAttribute('allow', 'usb; cross-origin-isolated;'); iframe.setAttribute('aria-hidden', 'true'); iframe.style.display = 'none'; From 0f05ed4758246bdf3efcdab610a3a87401f4a465 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 17 Jun 2022 14:34:37 +0200 Subject: [PATCH 145/175] Fix `workbench.action.submitComment` (#152464) Fixes #151739 --- src/vs/workbench/contrib/comments/browser/commentNode.ts | 6 ++++++ .../contrib/comments/browser/commentThreadWidget.ts | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index e37c1bfd5fe..d1d118f5c15 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -273,6 +273,12 @@ export class CommentNode extends Disposable { } } + async submitComment(): Promise { + if (this._commentEditor && this._commentFormActions) { + this._commentFormActions.triggerDefaultAction(); + } + } + private createReactionPicker(reactionGroup: languages.CommentReaction[]): ToggleReactionsAction { const toggleReactionAction = this._register(new ToggleReactionsAction(() => { if (toggleReactionActionViewItem) { diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts index c5fd1206a96..f4f972a1a00 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts @@ -18,7 +18,6 @@ import { ICommentService } from 'vs/workbench/contrib/comments/browser/commentSe import { CommentThreadBody } from 'vs/workbench/contrib/comments/browser/commentThreadBody'; import { CommentThreadHeader } from 'vs/workbench/contrib/comments/browser/commentThreadHeader'; import { CommentContextKeys } from 'vs/workbench/contrib/comments/common/commentContextKeys'; -import { CommentNode } from 'vs/workbench/contrib/comments/common/commentModel'; import { ICommentThreadWidget } from 'vs/workbench/contrib/comments/common/commentThreadWidget'; import { IColorTheme } from 'vs/platform/theme/common/themeService'; import { contrastBorder, focusBorder, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; @@ -273,7 +272,9 @@ export class CommentThreadWidget extends async submitComment() { const activeComment = this._body.activeComment; - if (activeComment && !(activeComment instanceof CommentNode)) { + if (activeComment) { + activeComment.submitComment(); + } else if ((this._commentReply?.getPendingComment()?.length ?? 0) > 0) { this._commentReply?.submitComment(); } } From 47652af0b6c5fb18133688708e41d4ef3ce5e239 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 17 Jun 2022 15:20:53 +0200 Subject: [PATCH 146/175] Improve env variable handling around extension host connection type (#152466) --- src/vs/server/node/extensionHostConnection.ts | 5 +- .../api/node/extensionHostProcess.ts | 9 +- .../extensions/common/extensionHostEnv.ts | 93 +++++++++++++++++++ .../nativeLocalProcessExtensionHost.ts | 3 +- .../localProcessExtensionHost.ts | 3 +- 5 files changed, 106 insertions(+), 7 deletions(-) create mode 100644 src/vs/workbench/services/extensions/common/extensionHostEnv.ts diff --git a/src/vs/server/node/extensionHostConnection.ts b/src/vs/server/node/extensionHostConnection.ts index 9e0a646f225..b2ed76583ce 100644 --- a/src/vs/server/node/extensionHostConnection.ts +++ b/src/vs/server/node/extensionHostConnection.ts @@ -22,6 +22,7 @@ import { logRemoteEntry } from 'vs/workbench/services/extensions/common/remoteCo import { removeDangerousEnvVariables } from 'vs/base/common/processes'; import { IExtensionHostStatusService } from 'vs/server/node/extensionHostStatusService'; import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { IPCExtHostConnection, writeExtHostConnection, SocketExtHostConnection } from 'vs/workbench/services/extensions/common/extensionHostEnv'; export async function buildUserEnvironment(startParamsEnv: { [key: string]: string | null } = {}, withUserShellEnvironment: boolean, language: string, isDebug: boolean, environmentService: IServerEnvironmentService, logService: ILogService): Promise { const nlsConfig = await getNLSConfiguration(language, environmentService.userDataPath); @@ -244,11 +245,11 @@ export class ExtensionHostConnection { let extHostNamedPipeServer: net.Server | null; if (this._canSendSocket) { - env['VSCODE_EXTHOST_WILL_SEND_SOCKET'] = 'true'; + writeExtHostConnection(new SocketExtHostConnection(), env); extHostNamedPipeServer = null; } else { const { namedPipeServer, pipeName } = await this._listenOnPipe(); - env['VSCODE_IPC_HOOK_EXTHOST'] = pipeName; + writeExtHostConnection(new IPCExtHostConnection(pipeName), env); extHostNamedPipeServer = namedPipeServer; } diff --git a/src/vs/workbench/api/node/extensionHostProcess.ts b/src/vs/workbench/api/node/extensionHostProcess.ts index 331b384e394..bad55575c87 100644 --- a/src/vs/workbench/api/node/extensionHostProcess.ts +++ b/src/vs/workbench/api/node/extensionHostProcess.ts @@ -24,6 +24,7 @@ import { ProcessTimeRunOnceScheduler } from 'vs/base/common/async'; import { boolean } from 'vs/editor/common/config/editorOptions'; import { createURITransformer } from 'vs/workbench/api/node/uriTransformer'; import { MessagePortMain } from 'electron'; +import { ExtHostConnectionType, readExtHostConnection } from 'vs/workbench/services/extensions/common/extensionHostEnv'; import 'vs/workbench/api/common/extHost.common.services'; import 'vs/workbench/api/node/extHost.node.services'; @@ -110,7 +111,9 @@ let onTerminate = function (reason: string) { }; function _createExtHostProtocol(): Promise { - if (process.env.VSCODE_WILL_SEND_MESSAGE_PORT) { + const extHostConnection = readExtHostConnection(process.env); + + if (extHostConnection.type === ExtHostConnectionType.MessagePort) { return new Promise((resolve, reject) => { @@ -139,7 +142,7 @@ function _createExtHostProtocol(): Promise { }); - } else if (process.env.VSCODE_EXTHOST_WILL_SEND_SOCKET) { + } else if (extHostConnection.type === ExtHostConnectionType.Socket) { return new Promise((resolve, reject) => { @@ -208,7 +211,7 @@ function _createExtHostProtocol(): Promise { } else { - const pipeName = process.env.VSCODE_IPC_HOOK_EXTHOST!; + const pipeName = extHostConnection.pipeName; return new Promise((resolve, reject) => { diff --git a/src/vs/workbench/services/extensions/common/extensionHostEnv.ts b/src/vs/workbench/services/extensions/common/extensionHostEnv.ts new file mode 100644 index 00000000000..9b952930ecd --- /dev/null +++ b/src/vs/workbench/services/extensions/common/extensionHostEnv.ts @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IProcessEnvironment } from 'vs/base/common/platform'; + +export const enum ExtHostConnectionType { + IPC = 1, + Socket = 2, + MessagePort = 3 +} + +/** + * The extension host will connect via named pipe / domain socket to its renderer. + */ +export class IPCExtHostConnection { + public static ENV_KEY = 'VSCODE_EXTHOST_IPC_HOOK'; + + public readonly type = ExtHostConnectionType.IPC; + + constructor( + public readonly pipeName: string + ) { } + + public serialize(env: IProcessEnvironment): void { + env[IPCExtHostConnection.ENV_KEY] = this.pipeName; + } +} + +/** + * The extension host will receive via nodejs IPC the socket to its renderer. + */ +export class SocketExtHostConnection { + public static ENV_KEY = 'VSCODE_EXTHOST_WILL_SEND_SOCKET'; + + public readonly type = ExtHostConnectionType.Socket; + + public serialize(env: IProcessEnvironment): void { + env[SocketExtHostConnection.ENV_KEY] = '1'; + } +} + +/** + * The extension host will receive via nodejs IPC the MessagePort to its renderer. + */ +export class MessagePortExtHostConnection { + public static ENV_KEY = 'VSCODE_WILL_SEND_MESSAGE_PORT'; + + public readonly type = ExtHostConnectionType.MessagePort; + + public serialize(env: IProcessEnvironment): void { + env[MessagePortExtHostConnection.ENV_KEY] = '1'; + } +} + +export type ExtHostConnection = IPCExtHostConnection | SocketExtHostConnection | MessagePortExtHostConnection; + +function clean(env: IProcessEnvironment): void { + delete env[IPCExtHostConnection.ENV_KEY]; + delete env[SocketExtHostConnection.ENV_KEY]; + delete env[MessagePortExtHostConnection.ENV_KEY]; +} + +/** + * Write `connection` into `env` and clean up `env`. + */ +export function writeExtHostConnection(connection: ExtHostConnection, env: IProcessEnvironment): void { + // Avoid having two different keys that might introduce amiguity or problems. + clean(env); + connection.serialize(env); +} + +/** + * Read `connection` from `env` and clean up `env`. + */ +export function readExtHostConnection(env: IProcessEnvironment): ExtHostConnection { + if (env[IPCExtHostConnection.ENV_KEY]) { + return cleanAndReturn(env, new IPCExtHostConnection(env[IPCExtHostConnection.ENV_KEY]!)); + } + if (env[SocketExtHostConnection.ENV_KEY]) { + return cleanAndReturn(env, new SocketExtHostConnection()); + } + if (env[MessagePortExtHostConnection.ENV_KEY]) { + return cleanAndReturn(env, new MessagePortExtHostConnection()); + } + throw new Error(`No connection information defined in environment!`); +} + +function cleanAndReturn(env: IProcessEnvironment, result: ExtHostConnection): ExtHostConnection { + clean(env); + return result; +} diff --git a/src/vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost.ts index d5bcf07f039..888db160609 100644 --- a/src/vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost.ts @@ -12,6 +12,7 @@ import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net'; import { createRandomIPCHandle, NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; import { IExtensionHostProcessOptions } from 'vs/platform/extensions/common/extensionHostStarter'; import { ILogService } from 'vs/platform/log/common/log'; +import { IPCExtHostConnection, writeExtHostConnection } from 'vs/workbench/services/extensions/common/extensionHostEnv'; import { createMessageOfType, MessageType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { ExtensionHostProcess, ExtHostMessagePortCommunication, IExtHostCommunication, SandboxLocalProcessExtensionHost } from 'vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost'; @@ -64,7 +65,7 @@ class ExtHostNamedPipeCommunication extends Disposable implements IExtHostCommun establishProtocol(prepared: INamedPipePreparedData, extensionHostProcess: ExtensionHostProcess, opts: IExtensionHostProcessOptions): Promise { const { namedPipeServer, pipeName } = prepared; - opts.env['VSCODE_IPC_HOOK_EXTHOST'] = pipeName; + writeExtHostConnection(new IPCExtHostConnection(pipeName), opts.env); return new Promise((resolve, reject) => { diff --git a/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts index cb7abfcbf53..2932c6be3ef 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts @@ -45,6 +45,7 @@ import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/use import { generateUuid } from 'vs/base/common/uuid'; import { acquirePort } from 'vs/base/parts/ipc/electron-sandbox/ipc.mp'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { MessagePortExtHostConnection, writeExtHostConnection } from 'vs/workbench/services/extensions/common/extensionHostEnv'; export interface ILocalProcessExtensionHostInitData { readonly autoStart: boolean; @@ -620,7 +621,7 @@ export class ExtHostMessagePortCommunication extends Disposable implements IExtH establishProtocol(prepared: void, extensionHostProcess: ExtensionHostProcess, opts: IExtensionHostProcessOptions): Promise { - opts.env['VSCODE_WILL_SEND_MESSAGE_PORT'] = 'true'; + writeExtHostConnection(new MessagePortExtHostConnection(), opts.env); // Get ready to acquire the message port from the shared process worker const portPromise = acquirePort(undefined /* we trigger the request via service call! */, opts.responseChannel, opts.responseNonce); From a10626ee067e69d02aea8cbb2c9756ad8ae62e2f Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 17 Jun 2022 15:36:49 +0200 Subject: [PATCH 147/175] No warning on unknown product icon name (#152468) --- .../workbench/services/themes/common/productIconThemeSchema.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/services/themes/common/productIconThemeSchema.ts b/src/vs/workbench/services/themes/common/productIconThemeSchema.ts index a4c26ca6d6a..f401690b666 100644 --- a/src/vs/workbench/services/themes/common/productIconThemeSchema.ts +++ b/src/vs/workbench/services/themes/common/productIconThemeSchema.ts @@ -79,8 +79,7 @@ const schema: IJSONSchema = { }, iconDefinitions: { description: nls.localize('schema.iconDefinitions', 'Association of icon name to a font character.'), - $ref: iconsSchemaId, - additionalProperties: false + $ref: iconsSchemaId } } }; From a6c39bc1fcd8152a1447a22b2f1bf99ed494331b Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 17 Jun 2022 15:51:15 +0200 Subject: [PATCH 148/175] Extract hover range computation to a separate method --- .../contrib/hover/browser/contentHover.ts | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/contentHover.ts b/src/vs/editor/contrib/hover/browser/contentHover.ts index bf93d3b9d94..6ea647ff75d 100644 --- a/src/vs/editor/contrib/hover/browser/contentHover.ts +++ b/src/vs/editor/contrib/hover/browser/contentHover.ts @@ -199,17 +199,7 @@ export class ContentHoverController extends Disposable { } private _renderMessages(anchor: HoverAnchor, messages: IHoverPart[]): void { - // update column from which to show - let renderColumn = Constants.MAX_SAFE_SMALL_INTEGER; - let highlightRange: Range = messages[0].range; - let forceShowAtRange: Range | null = null; - for (const msg of messages) { - renderColumn = Math.min(renderColumn, msg.range.startColumn); - highlightRange = Range.plusRange(highlightRange, msg.range); - if (msg.forceShowAtRange) { - forceShowAtRange = msg.range; - } - } + const { showAtPosition, showAtRange, highlightRange } = ContentHoverController.computeHoverRanges(anchor.range, messages); const disposables = new DisposableStore(); const statusBar = disposables.add(new EditorHoverStatusBar(this._keybindingService)); @@ -247,8 +237,8 @@ export class ContentHoverController extends Disposable { this._widget.showAt(fragment, new ContentHoverVisibleData( colorPicker, - forceShowAtRange ? forceShowAtRange.getStartPosition() : new Position(anchor.range.startLineNumber, renderColumn), - forceShowAtRange ? forceShowAtRange : highlightRange, + showAtPosition, + showAtRange, this._editor.getOption(EditorOption.hover).above, this._computer.shouldFocus, disposables @@ -262,6 +252,24 @@ export class ContentHoverController extends Disposable { description: 'content-hover-highlight', className: 'hoverHighlight' }); + + public static computeHoverRanges(anchorRange: Range, messages: IHoverPart[]) { + let renderColumn = Constants.MAX_SAFE_SMALL_INTEGER; + let highlightRange: Range = messages[0].range; + let forceShowAtRange: Range | null = null; + for (const msg of messages) { + renderColumn = Math.min(renderColumn, msg.range.startColumn); + highlightRange = Range.plusRange(highlightRange, msg.range); + if (msg.forceShowAtRange) { + forceShowAtRange = msg.range; + } + } + return { + showAtPosition: forceShowAtRange ? forceShowAtRange.getStartPosition() : new Position(anchorRange.startLineNumber, renderColumn), + showAtRange: forceShowAtRange ? forceShowAtRange : highlightRange, + highlightRange + }; + } } class ContentHoverVisibleData { From 5ca287646df9260abed6dc8afea9965f83d5e8a1 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 17 Jun 2022 16:02:21 +0200 Subject: [PATCH 149/175] Fixes #151235: Always render the hover on the line of the anchor even if the hover provider returns a larger hover range --- .../contrib/hover/browser/contentHover.ts | 18 +++++++++---- .../hover/test/browser/contentHover.test.ts | 27 +++++++++++++++++++ 2 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 src/vs/editor/contrib/hover/test/browser/contentHover.test.ts diff --git a/src/vs/editor/contrib/hover/browser/contentHover.ts b/src/vs/editor/contrib/hover/browser/contentHover.ts index 6ea647ff75d..586e15584c5 100644 --- a/src/vs/editor/contrib/hover/browser/contentHover.ts +++ b/src/vs/editor/contrib/hover/browser/contentHover.ts @@ -9,7 +9,6 @@ import { coalesce } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; -import { Constants } from 'vs/base/common/uint'; import { ContentWidgetPositionPreference, IActiveCodeEditor, ICodeEditor, IContentWidget, IContentWidgetPosition, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; @@ -254,19 +253,28 @@ export class ContentHoverController extends Disposable { }); public static computeHoverRanges(anchorRange: Range, messages: IHoverPart[]) { - let renderColumn = Constants.MAX_SAFE_SMALL_INTEGER; + // The anchor range is always on a single line + const anchorLineNumber = anchorRange.startLineNumber; + let renderStartColumn = anchorRange.startColumn; + let renderEndColumn = anchorRange.endColumn; let highlightRange: Range = messages[0].range; let forceShowAtRange: Range | null = null; + for (const msg of messages) { - renderColumn = Math.min(renderColumn, msg.range.startColumn); highlightRange = Range.plusRange(highlightRange, msg.range); + if (msg.range.startLineNumber === anchorLineNumber && msg.range.endLineNumber === anchorLineNumber) { + // this message has a range that is completely sitting on the line of the anchor + renderStartColumn = Math.min(renderStartColumn, msg.range.startColumn); + renderEndColumn = Math.max(renderEndColumn, msg.range.endColumn); + } if (msg.forceShowAtRange) { forceShowAtRange = msg.range; } } + return { - showAtPosition: forceShowAtRange ? forceShowAtRange.getStartPosition() : new Position(anchorRange.startLineNumber, renderColumn), - showAtRange: forceShowAtRange ? forceShowAtRange : highlightRange, + showAtPosition: forceShowAtRange ? forceShowAtRange.getStartPosition() : new Position(anchorRange.startLineNumber, renderStartColumn), + showAtRange: forceShowAtRange ? forceShowAtRange : new Range(anchorLineNumber, renderStartColumn, anchorLineNumber, renderEndColumn), highlightRange }; } diff --git a/src/vs/editor/contrib/hover/test/browser/contentHover.test.ts b/src/vs/editor/contrib/hover/test/browser/contentHover.test.ts new file mode 100644 index 00000000000..7e35cea943b --- /dev/null +++ b/src/vs/editor/contrib/hover/test/browser/contentHover.test.ts @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { ContentHoverController } from 'vs/editor/contrib/hover/browser/contentHover'; +import { Range } from 'vs/editor/common/core/range'; +import { Position } from 'vs/editor/common/core/position'; +import { IHoverPart } from 'vs/editor/contrib/hover/browser/hoverTypes'; + +suite('Content Hover', () => { + test('issue #151235: Gitlens hover shows up in the wrong place', () => { + const actual = ContentHoverController.computeHoverRanges( + new Range(5, 5, 5, 5), + [{ range: new Range(4, 1, 5, 6) }] + ); + assert.deepStrictEqual( + actual, + { + showAtPosition: new Position(5, 5), + showAtRange: new Range(5, 5, 5, 5), + highlightRange: new Range(4, 1, 5, 6) + } + ); + }); +}); From 2e91c26f1dd2278166dea0327c1ff8a87cd1730a Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 17 Jun 2022 16:15:55 +0200 Subject: [PATCH 150/175] Avoid old import syntax (#152471) --- .../inlineCompletions/test/browser/suggestWidgetModel.test.ts | 2 +- .../model/bracketPairColorizer/beforeEditPositionMapper.test.ts | 2 +- .../test/common/model/bracketPairColorizer/brackets.test.ts | 2 +- .../common/model/bracketPairColorizer/concat23Trees.test.ts | 2 +- .../model/bracketPairColorizer/getBracketPairsInRange.test.ts | 2 +- .../test/common/model/bracketPairColorizer/length.test.ts | 2 +- .../common/model/bracketPairColorizer/smallImmutableSet.test.ts | 2 +- .../test/common/model/bracketPairColorizer/tokenizer.test.ts | 2 +- src/vs/editor/test/common/viewModel/lineBreakData.test.ts | 2 +- src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts | 2 +- src/vs/workbench/contrib/extensions/browser/extensionEditor.ts | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts b/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts index 909e62f727c..6d93e308c95 100644 --- a/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts +++ b/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts @@ -27,7 +27,7 @@ import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { InMemoryStorageService, IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; -import assert = require('assert'); +import * as assert from 'assert'; import { createTextModel } from 'vs/editor/test/common/testTextModel'; import { ILabelService } from 'vs/platform/label/common/label'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/beforeEditPositionMapper.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/beforeEditPositionMapper.test.ts index 27fe72154f8..2fc81e3e176 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/beforeEditPositionMapper.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/beforeEditPositionMapper.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); +import * as assert from 'assert'; import { splitLines } from 'vs/base/common/strings'; import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/brackets.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/brackets.test.ts index 16c59a7bfe2..abf6200a8fa 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/brackets.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/brackets.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); +import * as assert from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { LanguageAgnosticBracketTokens } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/brackets'; import { SmallImmutableSet, DenseKeyProvider } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/smallImmutableSet'; diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/concat23Trees.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/concat23Trees.test.ts index f0b19c8a90f..c0c81ff2096 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/concat23Trees.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/concat23Trees.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); +import * as assert from 'assert'; import { AstNode, AstNodeKind, ListAstNode, TextAstNode } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/ast'; import { toLength } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/length'; import { concat23Trees } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/concat23Trees'; diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/getBracketPairsInRange.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/getBracketPairsInRange.test.ts index 4ee07976cb0..e74219407a6 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/getBracketPairsInRange.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/getBracketPairsInRange.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); +import * as assert from 'assert'; import { DisposableStore, disposeOnReturn } from 'vs/base/common/lifecycle'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/length.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/length.test.ts index 20e317d0843..b1932043ddd 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/length.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/length.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); +import * as assert from 'assert'; import { Length, lengthAdd, lengthDiffNonNegative, lengthToObj, toLength } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/length'; suite('Bracket Pair Colorizer - Length', () => { diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/smallImmutableSet.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/smallImmutableSet.test.ts index f36fe4db187..de3168139c5 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/smallImmutableSet.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/smallImmutableSet.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); +import * as assert from 'assert'; import { DenseKeyProvider, SmallImmutableSet } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/smallImmutableSet'; suite('Bracket Pair Colorizer - ImmutableSet', () => { diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/tokenizer.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/tokenizer.test.ts index 2615475d9f0..d1800b09f0d 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/tokenizer.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/tokenizer.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); +import * as assert from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { LanguageAgnosticBracketTokens } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/brackets'; import { Length, lengthAdd, lengthsToRange, lengthZero } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/length'; diff --git a/src/vs/editor/test/common/viewModel/lineBreakData.test.ts b/src/vs/editor/test/common/viewModel/lineBreakData.test.ts index 9b2715c1c23..9de24985297 100644 --- a/src/vs/editor/test/common/viewModel/lineBreakData.test.ts +++ b/src/vs/editor/test/common/viewModel/lineBreakData.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); +import * as assert from 'assert'; import { PositionAffinity } from 'vs/editor/common/model'; import { ModelDecorationInjectedTextOptions } from 'vs/editor/common/model/textModel'; import { ModelLineProjectionData } from 'vs/editor/common/modelLineProjectionData'; diff --git a/src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts b/src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts index 4947ad75e2e..4c2501f4692 100644 --- a/src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts +++ b/src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import type * as vscode from 'vscode'; -import assert = require('assert'); +import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; import { mock } from 'vs/base/test/common/mock'; import { IEditorTabDto, IEditorTabGroupDto, MainThreadEditorTabsShape, TabInputKind, TabModelOperationKind, TextInputDto } from 'vs/workbench/api/common/extHost.protocol'; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 13058dbd848..381096c914f 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -69,7 +69,7 @@ import { errorIcon, infoIcon, preReleaseIcon, verifiedPublisherIcon as verifiedP import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { ViewContainerLocation } from 'vs/workbench/common/views'; import { IExtensionGalleryService, IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; -import semver = require('vs/base/common/semver/semver'); +import * as semver from 'vs/base/common/semver/semver'; class NavBar extends Disposable { From 083cf01e105b1ed93f8fef4e3deae462d54df37c Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 17 Jun 2022 16:31:03 +0200 Subject: [PATCH 151/175] json indent pattern: handle escape characters (#152475) --- extensions/json/language-configuration.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/json/language-configuration.json b/extensions/json/language-configuration.json index 2dc97d50dbb..f9ec3fec781 100644 --- a/extensions/json/language-configuration.json +++ b/extensions/json/language-configuration.json @@ -16,7 +16,7 @@ { "open": "`", "close": "`", "notIn": ["string", "comment"] } ], "indentationRules": { - "increaseIndentPattern": "({+(?=([^\"]*\"[^\"]*\")*[^\"}]*$))|(\\[+(?=([^\"]*\"[^\"]*\")*[^\"\\]]*$))", + "increaseIndentPattern": "({+(?=((\\\\.|[^\"\\\\])*\"(\\\\.|[^\"\\\\])*\")*[^\"}]*)$)|(\\[+(?=((\\\\.|[^\"\\\\])*\"(\\\\.|[^\"\\\\])*\")*[^\"\\]]*)$)", "decreaseIndentPattern": "^\\s*[}\\]],?\\s*$" } } From b70e64d36bba461132055b1d6309f15d6d05eabb Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Fri, 17 Jun 2022 07:54:16 -0700 Subject: [PATCH 152/175] Increase color contrast for list highlight (#152351) Increase color contrast (Refs #152184) --- src/vs/platform/theme/common/colorRegistry.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index c167a9b97da..6d57911f12d 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -427,7 +427,7 @@ export const diffDiagonalFill = registerColor('diffEditor.diagonalFill', { dark: export const listFocusBackground = registerColor('list.focusBackground', { dark: null, light: null, hcDark: null, hcLight: null }, nls.localize('listFocusBackground', "List/Tree background color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listFocusForeground = registerColor('list.focusForeground', { dark: null, light: null, hcDark: null, hcLight: null }, nls.localize('listFocusForeground', "List/Tree foreground color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listFocusOutline = registerColor('list.focusOutline', { dark: focusBorder, light: focusBorder, hcDark: activeContrastBorder, hcLight: activeContrastBorder }, nls.localize('listFocusOutline', "List/Tree outline color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); -export const listActiveSelectionBackground = registerColor('list.activeSelectionBackground', { dark: '#094771', light: '#0060C0', hcDark: null, hcLight: Color.fromHex('#0F4A85').transparent(0.1) }, nls.localize('listActiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); +export const listActiveSelectionBackground = registerColor('list.activeSelectionBackground', { dark: '#04395E', light: '#0060C0', hcDark: null, hcLight: Color.fromHex('#0F4A85').transparent(0.1) }, nls.localize('listActiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listActiveSelectionForeground = registerColor('list.activeSelectionForeground', { dark: Color.white, light: Color.white, hcDark: null, hcLight: null }, nls.localize('listActiveSelectionForeground', "List/Tree foreground color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listActiveSelectionIconForeground = registerColor('list.activeSelectionIconForeground', { dark: null, light: null, hcDark: null, hcLight: null }, nls.localize('listActiveSelectionIconForeground', "List/Tree icon foreground color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listInactiveSelectionBackground = registerColor('list.inactiveSelectionBackground', { dark: '#37373D', light: '#E4E6F1', hcDark: null, hcLight: Color.fromHex('#0F4A85').transparent(0.1) }, nls.localize('listInactiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); @@ -438,8 +438,8 @@ export const listInactiveFocusOutline = registerColor('list.inactiveFocusOutline export const listHoverBackground = registerColor('list.hoverBackground', { dark: '#2A2D2E', light: '#F0F0F0', hcDark: null, hcLight: Color.fromHex('#0F4A85').transparent(0.1) }, nls.localize('listHoverBackground', "List/Tree background when hovering over items using the mouse.")); export const listHoverForeground = registerColor('list.hoverForeground', { dark: null, light: null, hcDark: null, hcLight: null }, nls.localize('listHoverForeground', "List/Tree foreground when hovering over items using the mouse.")); export const listDropBackground = registerColor('list.dropBackground', { dark: '#062F4A', light: '#D6EBFF', hcDark: null, hcLight: null }, nls.localize('listDropBackground', "List/Tree drag and drop background when moving items around using the mouse.")); -export const listHighlightForeground = registerColor('list.highlightForeground', { dark: '#18A3FF', light: '#0066BF', hcDark: focusBorder, hcLight: focusBorder }, nls.localize('highlight', 'List/Tree foreground color of the match highlights when searching inside the list/tree.')); -export const listFocusHighlightForeground = registerColor('list.focusHighlightForeground', { dark: listHighlightForeground, light: ifDefinedThenElse(listActiveSelectionBackground, listHighlightForeground, '#9DDDFF'), hcDark: listHighlightForeground, hcLight: listHighlightForeground }, nls.localize('listFocusHighlightForeground', 'List/Tree foreground color of the match highlights on actively focused items when searching inside the list/tree.')); +export const listHighlightForeground = registerColor('list.highlightForeground', { dark: '#2AAAFF', light: '#0066BF', hcDark: focusBorder, hcLight: focusBorder }, nls.localize('highlight', 'List/Tree foreground color of the match highlights when searching inside the list/tree.')); +export const listFocusHighlightForeground = registerColor('list.focusHighlightForeground', { dark: listHighlightForeground, light: ifDefinedThenElse(listActiveSelectionBackground, listHighlightForeground, '#BBE7FF'), hcDark: listHighlightForeground, hcLight: listHighlightForeground }, nls.localize('listFocusHighlightForeground', 'List/Tree foreground color of the match highlights on actively focused items when searching inside the list/tree.')); export const listInvalidItemForeground = registerColor('list.invalidItemForeground', { dark: '#B89500', light: '#B89500', hcDark: '#B89500', hcLight: '#B5200D' }, nls.localize('invalidItemForeground', 'List/Tree foreground color for invalid items, for example an unresolved root in explorer.')); export const listErrorForeground = registerColor('list.errorForeground', { dark: '#F88070', light: '#B01011', hcDark: null, hcLight: null }, nls.localize('listErrorForeground', 'Foreground color of list items containing errors.')); export const listWarningForeground = registerColor('list.warningForeground', { dark: '#CCA700', light: '#855F00', hcDark: null, hcLight: null }, nls.localize('listWarningForeground', 'Foreground color of list items containing warnings.')); From 252c65540d8714a29c0b3ab27ecc119538773d23 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 17 Jun 2022 17:12:27 +0200 Subject: [PATCH 153/175] Adopt the same export patterns in `vs/css` as in `vs/nls` and bring over tests (#152396) * Adopt the same export patterns in `vs/css` as in `vs/nls` and bring over tests * Fix problem with loading nodejs modules --- .eslintrc.json | 1 + build/gulpfile.editor.js | 2 +- src/vs/base/test/node/css.build.test.ts | 313 ++++++++++++++ src/vs/css.build.ts | 518 ++++++++++++------------ src/vs/css.ts | 162 ++++---- src/vs/nls.build.ts | 12 + src/vs/nls.ts | 8 +- 7 files changed, 674 insertions(+), 342 deletions(-) create mode 100644 src/vs/base/test/node/css.build.test.ts diff --git a/.eslintrc.json b/.eslintrc.json index e6493f3815e..3e1b349becc 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -277,6 +277,7 @@ // imports that are allowed in all /test/ files "when": "test", "allow": [ + "vs/css.build", "assert", "sinon", "sinon-test" diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index eb4f55f0801..fcc35c88b01 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -30,7 +30,7 @@ const editorEntryPoints = [ include: [], exclude: ['vs/css', 'vs/nls'], prepend: [ - { path: 'out-editor-build/vs/css.js' }, + { path: 'out-editor-build/vs/css.js', amdModuleId: 'vs/css' }, { path: 'out-editor-build/vs/nls.js', amdModuleId: 'vs/nls' } ], }, diff --git a/src/vs/base/test/node/css.build.test.ts b/src/vs/base/test/node/css.build.test.ts new file mode 100644 index 00000000000..9e7323b7f7a --- /dev/null +++ b/src/vs/base/test/node/css.build.test.ts @@ -0,0 +1,313 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { CSSPluginUtilities, rewriteUrls } from 'vs/css.build'; + +suite('CSSPlugin', () => { + + test('Utilities.pathOf', () => { + assert.strictEqual(CSSPluginUtilities.pathOf(''), ''); + assert.strictEqual(CSSPluginUtilities.pathOf('/a'), '/'); + assert.strictEqual(CSSPluginUtilities.pathOf('a/b/c.css'), 'a/b/'); + assert.strictEqual(CSSPluginUtilities.pathOf('a'), ''); + assert.strictEqual(CSSPluginUtilities.pathOf('a.com/a.css'), 'a.com/'); + assert.strictEqual(CSSPluginUtilities.pathOf('http://a.com/a.css'), 'http://a.com/'); + assert.strictEqual(CSSPluginUtilities.pathOf('https://a.com/a.css'), 'https://a.com/'); + assert.strictEqual(CSSPluginUtilities.pathOf('http://a.com/a/b/c.css'), 'http://a.com/a/b/'); + assert.strictEqual(CSSPluginUtilities.pathOf('https://a.com/a/b/c.css'), 'https://a.com/a/b/'); + assert.strictEqual(CSSPluginUtilities.pathOf('/a.css'), '/'); + assert.strictEqual(CSSPluginUtilities.pathOf('/a/b/c.css'), '/a/b/'); + }); + + test('Utilities.joinPaths', () => { + function mytest(a: string, b: string, expected: string) { + assert.strictEqual(CSSPluginUtilities.joinPaths(a, b), expected, '<' + a + '> + <' + b + '> = <' + expected + '>'); + } + mytest('', 'a.css', 'a.css'); + mytest('', './a.css', 'a.css'); + mytest('', '././././a.css', 'a.css'); + mytest('', './../a.css', '../a.css'); + mytest('', '../../a.css', '../../a.css'); + mytest('', '../../a/b/c.css', '../../a/b/c.css'); + mytest('/', 'a.css', '/a.css'); + mytest('/', './a.css', '/a.css'); + mytest('/', '././././a.css', '/a.css'); + mytest('/', './../a.css', '/a.css'); + mytest('/', '../../a.css', '/a.css'); + mytest('/', '../../a/b/c.css', '/a/b/c.css'); + mytest('x/y/z/', 'a.css', 'x/y/z/a.css'); + mytest('x/y/z/', './a.css', 'x/y/z/a.css'); + mytest('x/y/z/', '././././a.css', 'x/y/z/a.css'); + mytest('x/y/z/', './../a.css', 'x/y/a.css'); + mytest('x/y/z/', '../../a.css', 'x/a.css'); + mytest('x/y/z/', '../../a/b/c.css', 'x/a/b/c.css'); + + mytest('//a.com/', 'a.css', '//a.com/a.css'); + mytest('//a.com/', './a.css', '//a.com/a.css'); + mytest('//a.com/', '././././a.css', '//a.com/a.css'); + mytest('//a.com/', './../a.css', '//a.com/a.css'); + mytest('//a.com/', '../../a.css', '//a.com/a.css'); + mytest('//a.com/', '../../a/b/c.css', '//a.com/a/b/c.css'); + mytest('//a.com/x/y/z/', 'a.css', '//a.com/x/y/z/a.css'); + mytest('//a.com/x/y/z/', './a.css', '//a.com/x/y/z/a.css'); + mytest('//a.com/x/y/z/', '././././a.css', '//a.com/x/y/z/a.css'); + mytest('//a.com/x/y/z/', './../a.css', '//a.com/x/y/a.css'); + mytest('//a.com/x/y/z/', '../../a.css', '//a.com/x/a.css'); + mytest('//a.com/x/y/z/', '../../a/b/c.css', '//a.com/x/a/b/c.css'); + + mytest('http://a.com/', 'a.css', 'http://a.com/a.css'); + mytest('http://a.com/', './a.css', 'http://a.com/a.css'); + mytest('http://a.com/', '././././a.css', 'http://a.com/a.css'); + mytest('http://a.com/', './../a.css', 'http://a.com/a.css'); + mytest('http://a.com/', '../../a.css', 'http://a.com/a.css'); + mytest('http://a.com/', '../../a/b/c.css', 'http://a.com/a/b/c.css'); + mytest('http://a.com/x/y/z/', 'a.css', 'http://a.com/x/y/z/a.css'); + mytest('http://a.com/x/y/z/', './a.css', 'http://a.com/x/y/z/a.css'); + mytest('http://a.com/x/y/z/', '././././a.css', 'http://a.com/x/y/z/a.css'); + mytest('http://a.com/x/y/z/', './../a.css', 'http://a.com/x/y/a.css'); + mytest('http://a.com/x/y/z/', '../../a.css', 'http://a.com/x/a.css'); + mytest('http://a.com/x/y/z/', '../../a/b/c.css', 'http://a.com/x/a/b/c.css'); + + mytest('https://a.com/', 'a.css', 'https://a.com/a.css'); + mytest('https://a.com/', './a.css', 'https://a.com/a.css'); + mytest('https://a.com/', '././././a.css', 'https://a.com/a.css'); + mytest('https://a.com/', './../a.css', 'https://a.com/a.css'); + mytest('https://a.com/', '../../a.css', 'https://a.com/a.css'); + mytest('https://a.com/', '../../a/b/c.css', 'https://a.com/a/b/c.css'); + mytest('https://a.com/x/y/z/', 'a.css', 'https://a.com/x/y/z/a.css'); + mytest('https://a.com/x/y/z/', './a.css', 'https://a.com/x/y/z/a.css'); + mytest('https://a.com/x/y/z/', '././././a.css', 'https://a.com/x/y/z/a.css'); + mytest('https://a.com/x/y/z/', './../a.css', 'https://a.com/x/y/a.css'); + mytest('https://a.com/x/y/z/', '../../a.css', 'https://a.com/x/a.css'); + mytest('https://a.com/x/y/z/', '../../a/b/c.css', 'https://a.com/x/a/b/c.css'); + }); + + test('Utilities.commonPrefix', () => { + function mytest(a: string, b: string, expected: string) { + assert.strictEqual(CSSPluginUtilities.commonPrefix(a, b), expected, 'prefix(<' + a + '>, <' + b + '>) = <' + expected + '>'); + assert.strictEqual(CSSPluginUtilities.commonPrefix(b, a), expected, 'prefix(<' + b + '>, <' + a + '>) = <' + expected + '>'); + } + mytest('', '', ''); + mytest('x', '', ''); + mytest('x', 'x', 'x'); + mytest('aaaa', 'aaaa', 'aaaa'); + mytest('aaaaxyz', 'aaaa', 'aaaa'); + mytest('aaaaxyz', 'aaaatuv', 'aaaa'); + }); + + test('Utilities.commonFolderPrefix', () => { + function mytest(a: string, b: string, expected: string) { + assert.strictEqual(CSSPluginUtilities.commonFolderPrefix(a, b), expected, 'folderPrefix(<' + a + '>, <' + b + '>) = <' + expected + '>'); + assert.strictEqual(CSSPluginUtilities.commonFolderPrefix(b, a), expected, 'folderPrefix(<' + b + '>, <' + a + '>) = <' + expected + '>'); + } + mytest('', '', ''); + mytest('x', '', ''); + mytest('x', 'x', ''); + mytest('aaaa', 'aaaa', ''); + mytest('aaaaxyz', 'aaaa', ''); + mytest('aaaaxyz', 'aaaatuv', ''); + mytest('/', '/', '/'); + mytest('x/', '', ''); + mytest('x/', 'x/', 'x/'); + mytest('aaaa/', 'aaaa/', 'aaaa/'); + mytest('aaaa/axyz', 'aaaa/a', 'aaaa/'); + mytest('aaaa/axyz', 'aaaa/atuv', 'aaaa/'); + }); + + test('Utilities.relativePath', () => { + function mytest(a: string, b: string, expected: string) { + assert.strictEqual(CSSPluginUtilities.relativePath(a, b), expected, 'relativePath(<' + a + '>, <' + b + '>) = <' + expected + '>'); + } + mytest('', '', ''); + mytest('x', '', ''); + mytest('x', 'x', 'x'); + mytest('aaaa', 'aaaa', 'aaaa'); + mytest('aaaaxyz', 'aaaa', 'aaaa'); + mytest('aaaaxyz', 'aaaatuv', 'aaaatuv'); + + mytest('x/y/aaaaxyz', 'x/aaaatuv', '../aaaatuv'); + mytest('x/y/aaaaxyz', 'x/y/aaaatuv', 'aaaatuv'); + mytest('z/t/aaaaxyz', 'x/y/aaaatuv', '../../x/y/aaaatuv'); + mytest('aaaaxyz', 'x/y/aaaatuv', 'x/y/aaaatuv'); + + mytest('a', '/a', '/a'); + mytest('/', '/a', '/a'); + mytest('/a/b/c', '/a/b/c', '/a/b/c'); + mytest('/a/b', '/a/b/c/d', '/a/b/c/d'); + + mytest('a', 'http://a', 'http://a'); + mytest('/', 'http://a', 'http://a'); + mytest('/a/b/c', 'http://a/b/c', 'http://a/b/c'); + mytest('/a/b', 'http://a/b/c/d', 'http://a/b/c/d'); + + mytest('a', 'https://a', 'https://a'); + mytest('/', 'https://a', 'https://a'); + mytest('/a/b/c', 'https://a/b/c', 'https://a/b/c'); + mytest('/a/b', 'https://a/b/c/d', 'https://a/b/c/d'); + + mytest('x/', '', '../'); + mytest('x/', '', '../'); + mytest('x/', 'x/', ''); + mytest('x/a', 'x/a', 'a'); + }); + + test('Utilities.rewriteUrls', () => { + function mytest(originalFile: string, newFile: string, url: string, expected: string) { + assert.strictEqual(rewriteUrls(originalFile, newFile, 'sel { background:url(\'' + url + '\'); }'), 'sel { background:url(' + expected + '); }'); + assert.strictEqual(rewriteUrls(originalFile, newFile, 'sel { background:url(\"' + url + '\"); }'), 'sel { background:url(' + expected + '); }'); + assert.strictEqual(rewriteUrls(originalFile, newFile, 'sel { background:url(' + url + '); }'), 'sel { background:url(' + expected + '); }'); + } + + // img/img.png + mytest('a.css', 'b.css', 'img/img.png', 'img/img.png'); + mytest('a.css', 't/b.css', 'img/img.png', '../img/img.png'); + mytest('a.css', 'x/y/b.css', 'img/img.png', '../../img/img.png'); + mytest('x/a.css', 'b.css', 'img/img.png', 'x/img/img.png'); + mytest('x/y/a.css', 'b.css', 'img/img.png', 'x/y/img/img.png'); + mytest('x/y/a.css', 't/u/b.css', 'img/img.png', '../../x/y/img/img.png'); + mytest('x/y/a.css', 'x/u/b.css', 'img/img.png', '../y/img/img.png'); + mytest('x/y/a.css', 'x/y/b.css', 'img/img.png', 'img/img.png'); + mytest('/a.css', 'b.css', 'img/img.png', '/img/img.png'); + mytest('/a.css', 'x/b.css', 'img/img.png', '/img/img.png'); + mytest('/a.css', 'x/y/b.css', 'img/img.png', '/img/img.png'); + mytest('/x/a.css', 'b.css', 'img/img.png', '/x/img/img.png'); + mytest('/x/a.css', 'x/b.css', 'img/img.png', '/x/img/img.png'); + mytest('/x/a.css', 'x/y/b.css', 'img/img.png', '/x/img/img.png'); + mytest('/x/y/a.css', 'b.css', 'img/img.png', '/x/y/img/img.png'); + mytest('/x/y/a.css', 'x/b.css', 'img/img.png', '/x/y/img/img.png'); + mytest('/x/y/a.css', 'x/y/b.css', 'img/img.png', '/x/y/img/img.png'); + mytest('/a.css', '/b.css', 'img/img.png', '/img/img.png'); + mytest('/a.css', '/b.css', 'img/img.png', '/img/img.png'); + mytest('/x/a.css', '/b.css', 'img/img.png', '/x/img/img.png'); + mytest('/x/a.css', '/x/b.css', 'img/img.png', '/x/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'b.css', 'img/img.png', 'http://www.example.com/x/y/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'http://www.example2.com/b.css', 'img/img.png', 'http://www.example.com/x/y/img/img.png'); + mytest('https://www.example.com/x/y/a.css', 'b.css', 'img/img.png', 'https://www.example.com/x/y/img/img.png'); + + // ../img/img.png + mytest('a.css', 'b.css', '../img/img.png', '../img/img.png'); + mytest('a.css', 't/b.css', '../img/img.png', '../../img/img.png'); + mytest('a.css', 'x/y/b.css', '../img/img.png', '../../../img/img.png'); + mytest('x/a.css', 'b.css', '../img/img.png', 'img/img.png'); + mytest('x/y/a.css', 'b.css', '../img/img.png', 'x/img/img.png'); + mytest('x/y/a.css', 't/u/b.css', '../img/img.png', '../../x/img/img.png'); + mytest('x/y/a.css', 'x/u/b.css', '../img/img.png', '../img/img.png'); + mytest('x/y/a.css', 'x/y/b.css', '../img/img.png', '../img/img.png'); + mytest('/a.css', 'b.css', '../img/img.png', '/img/img.png'); + mytest('/a.css', 'x/b.css', '../img/img.png', '/img/img.png'); + mytest('/a.css', 'x/y/b.css', '../img/img.png', '/img/img.png'); + mytest('/x/a.css', 'b.css', '../img/img.png', '/img/img.png'); + mytest('/x/a.css', 'x/b.css', '../img/img.png', '/img/img.png'); + mytest('/x/a.css', 'x/y/b.css', '../img/img.png', '/img/img.png'); + mytest('/x/y/a.css', 'b.css', '../img/img.png', '/x/img/img.png'); + mytest('/x/y/a.css', 'x/b.css', '../img/img.png', '/x/img/img.png'); + mytest('/x/y/a.css', 'x/y/b.css', '../img/img.png', '/x/img/img.png'); + mytest('/a.css', '/b.css', '../img/img.png', '/img/img.png'); + mytest('/a.css', '/b.css', '../img/img.png', '/img/img.png'); + mytest('/x/a.css', '/b.css', '../img/img.png', '/img/img.png'); + mytest('/x/a.css', '/x/b.css', '../img/img.png', '/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'b.css', '../img/img.png', 'http://www.example.com/x/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'http://www.example2.com/b.css', '../img/img.png', 'http://www.example.com/x/img/img.png'); + mytest('https://www.example.com/x/y/a.css', 'b.css', '../img/img.png', 'https://www.example.com/x/img/img.png'); + + // /img/img.png + mytest('a.css', 'b.css', '/img/img.png', '/img/img.png'); + mytest('a.css', 't/b.css', '/img/img.png', '/img/img.png'); + mytest('a.css', 'x/y/b.css', '/img/img.png', '/img/img.png'); + mytest('x/a.css', 'b.css', '/img/img.png', '/img/img.png'); + mytest('x/y/a.css', 'b.css', '/img/img.png', '/img/img.png'); + mytest('x/y/a.css', 't/u/b.css', '/img/img.png', '/img/img.png'); + mytest('x/y/a.css', 'x/u/b.css', '/img/img.png', '/img/img.png'); + mytest('x/y/a.css', 'x/y/b.css', '/img/img.png', '/img/img.png'); + mytest('/a.css', 'b.css', '/img/img.png', '/img/img.png'); + mytest('/a.css', 'x/b.css', '/img/img.png', '/img/img.png'); + mytest('/a.css', 'x/y/b.css', '/img/img.png', '/img/img.png'); + mytest('/x/a.css', 'b.css', '/img/img.png', '/img/img.png'); + mytest('/x/a.css', 'x/b.css', '/img/img.png', '/img/img.png'); + mytest('/x/a.css', 'x/y/b.css', '/img/img.png', '/img/img.png'); + mytest('/x/y/a.css', 'b.css', '/img/img.png', '/img/img.png'); + mytest('/x/y/a.css', 'x/b.css', '/img/img.png', '/img/img.png'); + mytest('/x/y/a.css', 'x/y/b.css', '/img/img.png', '/img/img.png'); + mytest('/a.css', '/b.css', '/img/img.png', '/img/img.png'); + mytest('/a.css', '/b.css', '/img/img.png', '/img/img.png'); + mytest('/x/a.css', '/b.css', '/img/img.png', '/img/img.png'); + mytest('/x/a.css', '/x/b.css', '/img/img.png', '/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'b.css', '/img/img.png', 'http://www.example.com/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'http://www.example.com/x/y/b.css', '/img/img.png', 'http://www.example.com/img/img.png'); + mytest('https://www.example.com/x/y/a.css', 'b.css', '/img/img.png', 'https://www.example.com/img/img.png'); + + // http://example.com/img/img.png + mytest('a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('a.css', 't/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('a.css', 'x/y/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('x/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('x/y/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('x/y/a.css', 't/u/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('x/y/a.css', 'x/u/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('x/y/a.css', 'x/y/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/a.css', 'x/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/a.css', 'x/y/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/a.css', 'x/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/a.css', 'x/y/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/y/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/y/a.css', 'x/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/y/a.css', 'x/y/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/a.css', '/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/a.css', '/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/a.css', '/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/a.css', '/x/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'http://www.example.com/x/y/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('https://www.example.com/x/y/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + + + }); + + test('Utilities.rewriteUrls - quotes and spaces', () => { + assert.strictEqual(rewriteUrls('x/y/a.css', 't/u/b.css', 'sel { background:url(\'../img/img.png\'); }'), 'sel { background:url(../../x/img/img.png); }'); + assert.strictEqual(rewriteUrls('x/y/a.css', 't/u/b.css', 'sel { background:url(\t\'../img/img.png\'); }'), 'sel { background:url(../../x/img/img.png); }'); + assert.strictEqual(rewriteUrls('x/y/a.css', 't/u/b.css', 'sel { background:url( \'../img/img.png\'); }'), 'sel { background:url(../../x/img/img.png); }'); + assert.strictEqual(rewriteUrls('x/y/a.css', 't/u/b.css', 'sel { background:url(\'../img/img.png\'\t); }'), 'sel { background:url(../../x/img/img.png); }'); + assert.strictEqual(rewriteUrls('x/y/a.css', 't/u/b.css', 'sel { background:url(\'../img/img.png\' ); }'), 'sel { background:url(../../x/img/img.png); }'); + assert.strictEqual(rewriteUrls('x/y/a.css', 't/u/b.css', 'sel { background:url( \t \'../img/img.png\' \t); }'), 'sel { background:url(../../x/img/img.png); }'); + }); + + test('Bug 9601 - css should ignore data urls', () => { + const dataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAACHmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNC40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iPgogICAgICAgICA8ZGM6c3ViamVjdD4KICAgICAgICAgICAgPHJkZjpCYWcvPgogICAgICAgICA8L2RjOnN1YmplY3Q+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iPgogICAgICAgICA8eG1wOkNyZWF0b3JUb29sPkFkb2JlIEltYWdlUmVhZHk8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+ClC8oVQAAAGnSURBVDiNrZMxTxNxGMZ///9dZWns9a4dTHSABFiuCU5dGt2d9BsQls6GD2LCd2AiQQfixKIJE0ObdKIUSvDa5uLZihP0Sh+HOw3ipOUZ3zzvL2+e932NJBaRe7/Q8Uw5eMRrzXllDU8A5mJkLB+/TflQ+67JXb+5O0FUNS9deLckns/tn2A7hxtDawZvn37Vp78AX8rmxZLDewf89HGJ+fgKCrkrBeuXKPy44hbGN7e8eTbRZwALcFE2nuOy48j6zmaTYP8Qtxaia9A1uLWQYP8QZ7OJI+s7LjsXZeMBIIlLn61xgEbLnqadtiQp7Z0orq8rrq8r7Z1IkqadtkbLnsYBuvTZkpQBhgF7SRVFJRQ3QqW9bgY5P1V6fpoDu4oboaISSqpoGLD3GzAIOEqqaFBBURHF9TWlZxlEktKzruL6mqJi5kmqaBBwJIl7Wf+7LICBIYBSKGyE+LsHuCurzPo9Zv0e7soq/u4BhY0Qpfn68p6HCbHv4Q0qtBPfarLd1LR1nAVWzDNphJq2jjXZbirxrQYV2n0PT9Lih/Rwp/xLCz3T/+gnd2VVRJs/vngAAAAASUVORK5CYII='; + + function mytest(originalFile: string, newFile: string) { + assert.strictEqual(rewriteUrls(originalFile, newFile, 'sel { background:url(' + dataUrl + '); }'), 'sel { background:url(' + dataUrl + '); }'); + assert.strictEqual(rewriteUrls(originalFile, newFile, 'sel { background:url( \t' + dataUrl + '\t ); }'), 'sel { background:url(' + dataUrl + '); }'); + } + + mytest('a.css', 'b.css'); + mytest('a.css', 't/b.css'); + mytest('a.css', 'x/y/b.css'); + mytest('x/a.css', 'b.css'); + mytest('x/y/a.css', 'b.css'); + mytest('x/y/a.css', 't/u/b.css'); + mytest('x/y/a.css', 'x/u/b.css'); + mytest('x/y/a.css', 'x/y/b.css'); + mytest('/a.css', 'b.css'); + mytest('/a.css', 'x/b.css'); + mytest('/a.css', 'x/y/b.css'); + mytest('/x/a.css', 'b.css'); + mytest('/x/a.css', 'x/b.css'); + mytest('/x/a.css', 'x/y/b.css'); + mytest('/x/y/a.css', 'b.css'); + mytest('/x/y/a.css', 'x/b.css'); + mytest('/x/y/a.css', 'x/y/b.css'); + mytest('/a.css', '/b.css'); + mytest('/a.css', '/b.css'); + mytest('/x/a.css', '/b.css'); + mytest('/x/a.css', '/x/b.css'); + mytest('http://www.example.com/x/y/a.css', 'b.css'); + mytest('http://www.example.com/x/y/a.css', 'http://www.example.com/x/y/b.css'); + mytest('https://www.example.com/x/y/a.css', 'b.css'); + }); +}); diff --git a/src/vs/css.build.ts b/src/vs/css.build.ts index 2107d144622..d4b3fcc96b2 100644 --- a/src/vs/css.build.ts +++ b/src/vs/css.build.ts @@ -3,288 +3,302 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -namespace CSSBuildLoaderPlugin { +interface ICSSPluginConfig { + inlineResources?: boolean | 'base64'; + inlineResourcesLimit?: number; +} - interface ICSSPluginConfig { - inlineResources?: boolean | 'base64'; - inlineResourcesLimit?: number; +interface ICSSEntryPointData { + moduleName: string; + contents: string; + fsPath: string; +} + +// This file gets compiled also with the standalone editor, +// so we cannot depend on types from node.d.ts +interface INodeFS { + readFileSync(path: string, encoding: 'utf8'): string; + readFileSync(path: string): INodeBuffer; +} +interface INodeBuffer { + length: number; + toString(encoding?: 'base64'): string; +} +interface INodePath { + dirname(p: string): string; + join(...paths: string[]): string; +} + +const nodeReq = (module: string): T | undefined => { + if (typeof (require).__$__nodeRequire === 'function') { + return (require).__$__nodeRequire(module); } + return undefined; +}; - // This file gets compiled also with the standalone editor, - // so we cannot depend on types from node.d.ts - interface INodeFS { - readFileSync(path: string, encoding: 'utf8'): string; - readFileSync(path: string): INodeBuffer; +const fs = nodeReq('fs'); +const path = nodeReq('path'); + +let inlineResources: boolean | 'base64' = false; +let inlineResourcesLimit: number = 5000; + +const contentsMap: { [moduleName: string]: string } = {}; +const pathMap: { [moduleName: string]: string } = {}; +const entryPoints: { [entryPoint: string]: ICSSEntryPointData[] } = {}; +const inlinedResources: string[] = []; + +/** + * Invoked by the loader at build-time + */ +export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void { + if (!fs) { + throw new Error(`Cannot load files without 'fs'!`); } - interface INodeBuffer { - length: number; - toString(encoding?: 'base64'): string; + config = config || {}; + const myConfig = (config['vs/css'] || {}); + inlineResources = (typeof myConfig.inlineResources === 'undefined' ? false : myConfig.inlineResources); + inlineResourcesLimit = (myConfig.inlineResourcesLimit || 5000); + const cssUrl = req.toUrl(name + '.css'); + let contents = fs.readFileSync(cssUrl, 'utf8'); + if (contents.charCodeAt(0) === 65279 /* BOM */) { + // Remove BOM + contents = contents.substring(1); } - interface INodePath { - dirname(p: string): string; - join(...paths: string[]): string; + if (config.isBuild) { + contentsMap[name] = contents; + pathMap[name] = cssUrl; } + load({}); +} - const fs: INodeFS = (require).nodeRequire('fs'); - const path: INodePath = (require).nodeRequire('path'); +/** + * Invoked by the loader at build-time + */ +export function write(pluginName: string, moduleName: string, write: AMDLoader.IPluginWriteCallback): void { + const entryPoint = write.getEntryPoint(); - interface ICSSEntryPointData { - moduleName: string; - contents: string; - fsPath: string; - } + entryPoints[entryPoint] = entryPoints[entryPoint] || []; + entryPoints[entryPoint].push({ + moduleName: moduleName, + contents: contentsMap[moduleName], + fsPath: pathMap[moduleName], + }); - export class CSSPlugin implements AMDLoader.ILoaderPlugin { + write.asModule(pluginName + '!' + moduleName, + 'define([\'vs/css!' + entryPoint + '\'], {});' + ); +} - private inlineResources: boolean | 'base64' = false; - private inlineResourcesLimit: number = 5000; - - private readonly _contentsMap: { [moduleName: string]: string } = {}; - private readonly _pathMap: { [moduleName: string]: string } = {}; - private readonly _entryPoints: { [entryPoint: string]: ICSSEntryPointData[] } = {}; - private readonly _inlinedResources: string[] = []; - - public load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void { - config = config || {}; - const myConfig = (config['vs/css'] || {}); - this.inlineResources = (typeof myConfig.inlineResources === 'undefined' ? false : myConfig.inlineResources); - this.inlineResourcesLimit = (myConfig.inlineResourcesLimit || 5000); - const cssUrl = req.toUrl(name + '.css'); - let contents = fs.readFileSync(cssUrl, 'utf8'); - if (contents.charCodeAt(0) === 65279 /* BOM */) { - // Remove BOM - contents = contents.substring(1); - } - if (config.isBuild) { - this._contentsMap[name] = contents; - this._pathMap[name] = cssUrl; - } - load({}); - } - - public write(pluginName: string, moduleName: string, write: AMDLoader.IPluginWriteCallback): void { - const entryPoint = write.getEntryPoint(); - - this._entryPoints[entryPoint] = this._entryPoints[entryPoint] || []; - this._entryPoints[entryPoint].push({ - moduleName: moduleName, - contents: this._contentsMap[moduleName], - fsPath: this._pathMap[moduleName], - }); - - write.asModule(pluginName + '!' + moduleName, - 'define([\'vs/css!' + entryPoint + '\'], {});' - ); - } - - public writeFile(pluginName: string, moduleName: string, req: AMDLoader.IRelativeRequire, write: AMDLoader.IPluginWriteFileCallback, config: AMDLoader.IConfigurationOptions): void { - if (this._entryPoints && this._entryPoints.hasOwnProperty(moduleName)) { - const fileName = req.toUrl(moduleName + '.css'); - const contents = [ - '/*---------------------------------------------------------', - ' * Copyright (c) Microsoft Corporation. All rights reserved.', - ' *--------------------------------------------------------*/' - ], - entries = this._entryPoints[moduleName]; - for (let i = 0; i < entries.length; i++) { - if (this.inlineResources) { - contents.push(this._rewriteOrInlineUrls(entries[i].fsPath, entries[i].moduleName, moduleName, entries[i].contents, this.inlineResources === 'base64', this.inlineResourcesLimit)); - } else { - contents.push(this._rewriteUrls(entries[i].moduleName, moduleName, entries[i].contents)); - } - } - write(fileName, contents.join('\r\n')); - } - } - - public getInlinedResources(): string[] { - return this._inlinedResources || []; - } - - private _rewriteOrInlineUrls(originalFileFSPath: string, originalFile: string, newFile: string, contents: string, forceBase64: boolean, inlineByteLimit: number): string { - return Utilities.replaceURL(contents, (url) => { - if (/\.(svg|png)$/.test(url)) { - const fsPath = path.join(path.dirname(originalFileFSPath), url); - const fileContents = fs.readFileSync(fsPath); - - if (fileContents.length < inlineByteLimit) { - const normalizedFSPath = fsPath.replace(/\\/g, '/'); - this._inlinedResources.push(normalizedFSPath); - - const MIME = /\.svg$/.test(url) ? 'image/svg+xml' : 'image/png'; - let DATA = ';base64,' + fileContents.toString('base64'); - - if (!forceBase64 && /\.svg$/.test(url)) { - // .svg => url encode as explained at https://codepen.io/tigt/post/optimizing-svgs-in-data-uris - const newText = fileContents.toString() - .replace(/"/g, '\'') - .replace(/%/g, '%25') - .replace(//g, '%3E') - .replace(/&/g, '%26') - .replace(/#/g, '%23') - .replace(/\s+/g, ' '); - const encodedData = ',' + newText; - if (encodedData.length < DATA.length) { - DATA = encodedData; - } - } - return '"data:' + MIME + DATA + '"'; - } - } - - const absoluteUrl = Utilities.joinPaths(Utilities.pathOf(originalFile), url); - return Utilities.relativePath(newFile, absoluteUrl); - }); - } - - private _rewriteUrls(originalFile: string, newFile: string, contents: string): string { - return Utilities.replaceURL(contents, (url) => { - const absoluteUrl = Utilities.joinPaths(Utilities.pathOf(originalFile), url); - return Utilities.relativePath(newFile, absoluteUrl); - }); - } - } - - export class Utilities { - - public static startsWith(haystack: string, needle: string): boolean { - return haystack.length >= needle.length && haystack.substr(0, needle.length) === needle; - } - - /** - * Find the path of a file. - */ - public static pathOf(filename: string): string { - const lastSlash = filename.lastIndexOf('/'); - if (lastSlash !== -1) { - return filename.substr(0, lastSlash + 1); +/** + * Invoked by the loader at build-time + */ +export function writeFile(pluginName: string, moduleName: string, req: AMDLoader.IRelativeRequire, write: AMDLoader.IPluginWriteFileCallback, config: AMDLoader.IConfigurationOptions): void { + if (entryPoints && entryPoints.hasOwnProperty(moduleName)) { + const fileName = req.toUrl(moduleName + '.css'); + const contents = [ + '/*---------------------------------------------------------', + ' * Copyright (c) Microsoft Corporation. All rights reserved.', + ' *--------------------------------------------------------*/' + ], + entries = entryPoints[moduleName]; + for (let i = 0; i < entries.length; i++) { + if (inlineResources) { + contents.push(rewriteOrInlineUrls(entries[i].fsPath, entries[i].moduleName, moduleName, entries[i].contents, inlineResources === 'base64', inlineResourcesLimit)); } else { - return ''; + contents.push(rewriteUrls(entries[i].moduleName, moduleName, entries[i].contents)); + } + } + write(fileName, contents.join('\r\n')); + } +} + +export function getInlinedResources(): string[] { + return inlinedResources || []; +} + +function rewriteOrInlineUrls(originalFileFSPath: string, originalFile: string, newFile: string, contents: string, forceBase64: boolean, inlineByteLimit: number): string { + if (!fs || !path) { + throw new Error(`Cannot rewrite or inline urls without 'fs' or 'path'!`); + } + return CSSPluginUtilities.replaceURL(contents, (url) => { + if (/\.(svg|png)$/.test(url)) { + const fsPath = path.join(path.dirname(originalFileFSPath), url); + const fileContents = fs.readFileSync(fsPath); + + if (fileContents.length < inlineByteLimit) { + const normalizedFSPath = fsPath.replace(/\\/g, '/'); + inlinedResources.push(normalizedFSPath); + + const MIME = /\.svg$/.test(url) ? 'image/svg+xml' : 'image/png'; + let DATA = ';base64,' + fileContents.toString('base64'); + + if (!forceBase64 && /\.svg$/.test(url)) { + // .svg => url encode as explained at https://codepen.io/tigt/post/optimizing-svgs-in-data-uris + const newText = fileContents.toString() + .replace(/"/g, '\'') + .replace(/%/g, '%25') + .replace(//g, '%3E') + .replace(/&/g, '%26') + .replace(/#/g, '%23') + .replace(/\s+/g, ' '); + const encodedData = ',' + newText; + if (encodedData.length < DATA.length) { + DATA = encodedData; + } + } + return '"data:' + MIME + DATA + '"'; } } - /** - * A conceptual a + b for paths. - * Takes into account if `a` contains a protocol. - * Also normalizes the result: e.g.: a/b/ + ../c => a/c - */ - public static joinPaths(a: string, b: string): string { + const absoluteUrl = CSSPluginUtilities.joinPaths(CSSPluginUtilities.pathOf(originalFile), url); + return CSSPluginUtilities.relativePath(newFile, absoluteUrl); + }); +} - function findSlashIndexAfterPrefix(haystack: string, prefix: string): number { - if (Utilities.startsWith(haystack, prefix)) { - return Math.max(prefix.length, haystack.indexOf('/', prefix.length)); - } - return 0; +export function rewriteUrls(originalFile: string, newFile: string, contents: string): string { + return CSSPluginUtilities.replaceURL(contents, (url) => { + const absoluteUrl = CSSPluginUtilities.joinPaths(CSSPluginUtilities.pathOf(originalFile), url); + return CSSPluginUtilities.relativePath(newFile, absoluteUrl); + }); +} + +export class CSSPluginUtilities { + + public static startsWith(haystack: string, needle: string): boolean { + return haystack.length >= needle.length && haystack.substr(0, needle.length) === needle; + } + + /** + * Find the path of a file. + */ + public static pathOf(filename: string): string { + const lastSlash = filename.lastIndexOf('/'); + if (lastSlash !== -1) { + return filename.substr(0, lastSlash + 1); + } else { + return ''; + } + } + + /** + * A conceptual a + b for paths. + * Takes into account if `a` contains a protocol. + * Also normalizes the result: e.g.: a/b/ + ../c => a/c + */ + public static joinPaths(a: string, b: string): string { + + function findSlashIndexAfterPrefix(haystack: string, prefix: string): number { + if (CSSPluginUtilities.startsWith(haystack, prefix)) { + return Math.max(prefix.length, haystack.indexOf('/', prefix.length)); } + return 0; + } - let aPathStartIndex = 0; - aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, '//'); - aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'http://'); - aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'https://'); + let aPathStartIndex = 0; + aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, '//'); + aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'http://'); + aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'https://'); - function pushPiece(pieces: string[], piece: string): void { - if (piece === './') { + function pushPiece(pieces: string[], piece: string): void { + if (piece === './') { + // Ignore + return; + } + if (piece === '../') { + const prevPiece = (pieces.length > 0 ? pieces[pieces.length - 1] : null); + if (prevPiece && prevPiece === '/') { // Ignore return; } - if (piece === '../') { - const prevPiece = (pieces.length > 0 ? pieces[pieces.length - 1] : null); - if (prevPiece && prevPiece === '/') { - // Ignore - return; - } - if (prevPiece && prevPiece !== '../') { - // Pop - pieces.pop(); - return; - } - } - // Push - pieces.push(piece); - } - - function push(pieces: string[], path: string): void { - while (path.length > 0) { - const slashIndex = path.indexOf('/'); - const piece = (slashIndex >= 0 ? path.substring(0, slashIndex + 1) : path); - path = (slashIndex >= 0 ? path.substring(slashIndex + 1) : ''); - pushPiece(pieces, piece); + if (prevPiece && prevPiece !== '../') { + // Pop + pieces.pop(); + return; } } - - let pieces: string[] = []; - push(pieces, a.substr(aPathStartIndex)); - if (b.length > 0 && b.charAt(0) === '/') { - pieces = []; - } - push(pieces, b); - - return a.substring(0, aPathStartIndex) + pieces.join(''); + // Push + pieces.push(piece); } - public static commonPrefix(str1: string, str2: string): string { - const len = Math.min(str1.length, str2.length); - for (let i = 0; i < len; i++) { - if (str1.charCodeAt(i) !== str2.charCodeAt(i)) { - return str1.substring(0, i); - } + function push(pieces: string[], path: string): void { + while (path.length > 0) { + const slashIndex = path.indexOf('/'); + const piece = (slashIndex >= 0 ? path.substring(0, slashIndex + 1) : path); + path = (slashIndex >= 0 ? path.substring(slashIndex + 1) : ''); + pushPiece(pieces, piece); } - return str1.substring(0, len); } - public static commonFolderPrefix(fromPath: string, toPath: string): string { - const prefix = Utilities.commonPrefix(fromPath, toPath); - const slashIndex = prefix.lastIndexOf('/'); - if (slashIndex === -1) { - return ''; - } - return prefix.substring(0, slashIndex + 1); + let pieces: string[] = []; + push(pieces, a.substr(aPathStartIndex)); + if (b.length > 0 && b.charAt(0) === '/') { + pieces = []; } + push(pieces, b); - public static relativePath(fromPath: string, toPath: string): string { - if (Utilities.startsWith(toPath, '/') || Utilities.startsWith(toPath, 'http://') || Utilities.startsWith(toPath, 'https://')) { - return toPath; - } - - // Ignore common folder prefix - const prefix = Utilities.commonFolderPrefix(fromPath, toPath); - fromPath = fromPath.substr(prefix.length); - toPath = toPath.substr(prefix.length); - - const upCount = fromPath.split('/').length; - let result = ''; - for (let i = 1; i < upCount; i++) { - result += '../'; - } - return result + toPath; - } - - public static replaceURL(contents: string, replacer: (url: string) => string): string { - // Use ")" as the terminator as quotes are oftentimes not used at all - return contents.replace(/url\(\s*([^\)]+)\s*\)?/g, (_: string, ...matches: string[]) => { - let url = matches[0]; - // Eliminate starting quotes (the initial whitespace is not captured) - if (url.charAt(0) === '"' || url.charAt(0) === '\'') { - url = url.substring(1); - } - // The ending whitespace is captured - while (url.length > 0 && (url.charAt(url.length - 1) === ' ' || url.charAt(url.length - 1) === '\t')) { - url = url.substring(0, url.length - 1); - } - // Eliminate ending quotes - if (url.charAt(url.length - 1) === '"' || url.charAt(url.length - 1) === '\'') { - url = url.substring(0, url.length - 1); - } - - if (!Utilities.startsWith(url, 'data:') && !Utilities.startsWith(url, 'http://') && !Utilities.startsWith(url, 'https://')) { - url = replacer(url); - } - - return 'url(' + url + ')'; - }); - } + return a.substring(0, aPathStartIndex) + pieces.join(''); } - define('vs/css', new CSSPlugin()); + public static commonPrefix(str1: string, str2: string): string { + const len = Math.min(str1.length, str2.length); + for (let i = 0; i < len; i++) { + if (str1.charCodeAt(i) !== str2.charCodeAt(i)) { + return str1.substring(0, i); + } + } + return str1.substring(0, len); + } + + public static commonFolderPrefix(fromPath: string, toPath: string): string { + const prefix = CSSPluginUtilities.commonPrefix(fromPath, toPath); + const slashIndex = prefix.lastIndexOf('/'); + if (slashIndex === -1) { + return ''; + } + return prefix.substring(0, slashIndex + 1); + } + + public static relativePath(fromPath: string, toPath: string): string { + if (CSSPluginUtilities.startsWith(toPath, '/') || CSSPluginUtilities.startsWith(toPath, 'http://') || CSSPluginUtilities.startsWith(toPath, 'https://')) { + return toPath; + } + + // Ignore common folder prefix + const prefix = CSSPluginUtilities.commonFolderPrefix(fromPath, toPath); + fromPath = fromPath.substr(prefix.length); + toPath = toPath.substr(prefix.length); + + const upCount = fromPath.split('/').length; + let result = ''; + for (let i = 1; i < upCount; i++) { + result += '../'; + } + return result + toPath; + } + + public static replaceURL(contents: string, replacer: (url: string) => string): string { + // Use ")" as the terminator as quotes are oftentimes not used at all + return contents.replace(/url\(\s*([^\)]+)\s*\)?/g, (_: string, ...matches: string[]) => { + let url = matches[0]; + // Eliminate starting quotes (the initial whitespace is not captured) + if (url.charAt(0) === '"' || url.charAt(0) === '\'') { + url = url.substring(1); + } + // The ending whitespace is captured + while (url.length > 0 && (url.charAt(url.length - 1) === ' ' || url.charAt(url.length - 1) === '\t')) { + url = url.substring(0, url.length - 1); + } + // Eliminate ending quotes + if (url.charAt(url.length - 1) === '"' || url.charAt(url.length - 1) === '\'') { + url = url.substring(0, url.length - 1); + } + + if (!CSSPluginUtilities.startsWith(url, 'data:') && !CSSPluginUtilities.startsWith(url, 'http://') && !CSSPluginUtilities.startsWith(url, 'https://')) { + url = replacer(url); + } + + return 'url(' + url + ')'; + }); + } } diff --git a/src/vs/css.ts b/src/vs/css.ts index 942040b4af5..4a5ea48d174 100644 --- a/src/vs/css.ts +++ b/src/vs/css.ts @@ -3,91 +3,79 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - -namespace CSSLoaderPlugin { - - interface ICSSPluginConfig { - disabled?: boolean; - } - - class BrowserCSSLoader { - - public load(name: string, cssUrl: string, callback: () => void, errorback: (err: any) => void): void { - if (this._linkTagExists(name, cssUrl)) { - callback(); - return; - } - this._createLinkTag(name, cssUrl, callback, errorback); - } - - private _linkTagExists(name: string, cssUrl: string): boolean { - const links = document.getElementsByTagName('link'); - for (let i = 0, len = links.length; i < len; i++) { - const nameAttr = links[i].getAttribute('data-name'); - const hrefAttr = links[i].getAttribute('href'); - if (nameAttr === name || hrefAttr === cssUrl) { - return true; - } - } - return false; - } - - private _createLinkTag(name: string, cssUrl: string, callback: () => void, errorback: (err: any) => void): void { - const linkNode = document.createElement('link'); - linkNode.setAttribute('rel', 'stylesheet'); - linkNode.setAttribute('type', 'text/css'); - linkNode.setAttribute('data-name', name); - - this._attachListeners(name, linkNode, callback, errorback); - linkNode.setAttribute('href', cssUrl); - - const head = document.head || document.getElementsByTagName('head')[0]; - head.appendChild(linkNode); - } - - private _attachListeners(name: string, linkNode: HTMLLinkElement, callback: () => void, errorback: (err: any) => void): void { - const unbind = () => { - linkNode.removeEventListener('load', loadEventListener); - linkNode.removeEventListener('error', errorEventListener); - }; - const loadEventListener = (e: any) => { - unbind(); - callback(); - }; - const errorEventListener = (e: any) => { - unbind(); - errorback(e); - }; - linkNode.addEventListener('load', loadEventListener); - linkNode.addEventListener('error', errorEventListener); - } - } - - export class CSSPlugin implements AMDLoader.ILoaderPlugin { - - private _cssLoader = new BrowserCSSLoader(); - - public load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void { - config = config || {}; - const cssConfig = (config['vs/css'] || {}); - - if (cssConfig.disabled) { - // the plugin is asked to not create any style sheets - load({}); - return; - } - - const cssUrl = req.toUrl(name + '.css'); - this._cssLoader.load(name, cssUrl, () => { - load({}); - }, (err: any) => { - if (typeof load.error === 'function') { - load.error('Could not find ' + cssUrl + '.'); - } - }); - } - } - - define('vs/css', new CSSPlugin()); +interface ICSSPluginConfig { + disabled?: boolean; +} + +/** + * Invoked by the loader at run-time + */ +export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void { + config = config || {}; + const cssConfig = (config['vs/css'] || {}); + + if (cssConfig.disabled) { + // the plugin is asked to not create any style sheets + load({}); + return; + } + + const cssUrl = req.toUrl(name + '.css'); + loadCSS(name, cssUrl, () => { + load({}); + }, (err: any) => { + if (typeof load.error === 'function') { + load.error('Could not find ' + cssUrl + '.'); + } + }); +} + +function loadCSS(name: string, cssUrl: string, callback: () => void, errorback: (err: any) => void): void { + if (linkTagExists(name, cssUrl)) { + callback(); + return; + } + createLinkTag(name, cssUrl, callback, errorback); +} + +function linkTagExists(name: string, cssUrl: string): boolean { + const links = document.getElementsByTagName('link'); + for (let i = 0, len = links.length; i < len; i++) { + const nameAttr = links[i].getAttribute('data-name'); + const hrefAttr = links[i].getAttribute('href'); + if (nameAttr === name || hrefAttr === cssUrl) { + return true; + } + } + return false; +} + +function createLinkTag(name: string, cssUrl: string, callback: () => void, errorback: (err: any) => void): void { + const linkNode = document.createElement('link'); + linkNode.setAttribute('rel', 'stylesheet'); + linkNode.setAttribute('type', 'text/css'); + linkNode.setAttribute('data-name', name); + + attachListeners(name, linkNode, callback, errorback); + linkNode.setAttribute('href', cssUrl); + + const head = document.head || document.getElementsByTagName('head')[0]; + head.appendChild(linkNode); +} + +function attachListeners(name: string, linkNode: HTMLLinkElement, callback: () => void, errorback: (err: any) => void): void { + const unbind = () => { + linkNode.removeEventListener('load', loadEventListener); + linkNode.removeEventListener('error', errorEventListener); + }; + const loadEventListener = (e: any) => { + unbind(); + callback(); + }; + const errorEventListener = (e: any) => { + unbind(); + errorback(e); + }; + linkNode.addEventListener('load', loadEventListener); + linkNode.addEventListener('error', errorEventListener); } diff --git a/src/vs/nls.build.ts b/src/vs/nls.build.ts index 067fba651e3..f73bdc2d1b4 100644 --- a/src/vs/nls.build.ts +++ b/src/vs/nls.build.ts @@ -16,6 +16,9 @@ export function localize(data: ILocalizeInfo | string, message: string, ...args: throw new Error(`Not supported at build time!`); } +/** + * Invoked by the loader at build-time + */ export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void { if (!name || name.length === 0) { load({ localize }); @@ -28,6 +31,9 @@ export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoa } } +/** + * Invoked by the loader at build-time + */ export function write(pluginName: string, moduleName: string, write: AMDLoader.IPluginWriteCallback): void { const entryPoint = write.getEntryPoint(); @@ -39,6 +45,9 @@ export function write(pluginName: string, moduleName: string, write: AMDLoader.I } } +/** + * Invoked by the loader at build-time + */ export function writeFile(pluginName: string, moduleName: string, req: AMDLoader.IRelativeRequire, write: AMDLoader.IPluginWriteFileCallback, config: AMDLoader.IConfigurationOptions): void { if (entryPoints.hasOwnProperty(moduleName)) { const fileName = req.toUrl(moduleName + '.nls.js'); @@ -59,6 +68,9 @@ export function writeFile(pluginName: string, moduleName: string, req: AMDLoader } } +/** + * Invoked by the loader at build-time + */ export function finishBuild(write: AMDLoader.IPluginWriteFileCallback): void { write('nls.metadata.json', JSON.stringify({ keys: buildMapKeys, diff --git a/src/vs/nls.ts b/src/vs/nls.ts index ebe6f7038ef..2a257b368d4 100644 --- a/src/vs/nls.ts +++ b/src/vs/nls.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -'use strict'; - let isPseudo = (typeof document !== 'undefined' && document.location && document.location.hash.indexOf('pseudo=true') >= 0); const DEFAULT_TAG = 'i-default'; @@ -115,12 +113,18 @@ export function setPseudoTranslation(value: boolean) { isPseudo = value; } +/** + * Invoked in a built product at run-time + */ export function create(key: string, data: IBundledStrings): IConsumerAPI { return { localize: createScopedLocalize(data[key]) }; } +/** + * Invoked by the loader at run-time + */ export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void { config = config || {}; if (!name || name.length === 0) { From 1ca9725a3b4264141e10d6a7697b74eaba955b58 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 17 Jun 2022 08:18:49 -0700 Subject: [PATCH 154/175] Disable terminal smoke tests on desktop/remote Fixes #152451 --- test/smoke/src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 3836601534f..d888604ca6c 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -400,7 +400,7 @@ describe(`VSCode Smoke Tests (${opts.web ? 'Web' : 'Electron'})`, () => { setupSearchTests(logger); setupNotebookTests(logger); setupLanguagesTests(logger); - setupTerminalTests(logger); + if (opts.web) { setupTerminalTests(logger); } // Not stable on desktop/remote https://github.com/microsoft/vscode/issues/146811 setupStatusbarTests(logger); if (quality !== Quality.Dev && quality !== Quality.OSS) { setupExtensionTests(logger); } setupMultirootTests(logger); From 1ed268719ed6eca48d3faa146dac2d4b1953e024 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 17 Jun 2022 08:31:00 -0700 Subject: [PATCH 155/175] xterm-addon-webgl@0.12.0-beta.39 Fixes #152436 --- package.json | 4 ++-- remote/package.json | 4 ++-- remote/web/package.json | 2 +- remote/web/yarn.lock | 8 ++++---- remote/yarn.lock | 16 ++++++++-------- yarn.lock | 16 ++++++++-------- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/package.json b/package.json index 2111f09eb02..ef009c0df53 100644 --- a/package.json +++ b/package.json @@ -89,9 +89,9 @@ "vscode-textmate": "7.0.1", "xterm": "4.19.0-beta.60", "xterm-addon-search": "0.9.0-beta.39", - "xterm-addon-serialize": "0.7.0-beta.12", + "xterm-addon-serialize": "0.7.0-beta.13", "xterm-addon-unicode11": "0.4.0-beta.3", - "xterm-addon-webgl": "0.12.0-beta.39", + "xterm-addon-webgl": "0.12.0-beta.41", "xterm-headless": "4.19.0-beta.60", "yauzl": "^2.9.2", "yazl": "^2.4.3" diff --git a/remote/package.json b/remote/package.json index 0544947abb0..e6fdc7a871b 100644 --- a/remote/package.json +++ b/remote/package.json @@ -28,9 +28,9 @@ "vscode-textmate": "7.0.1", "xterm": "4.19.0-beta.60", "xterm-addon-search": "0.9.0-beta.39", - "xterm-addon-serialize": "0.7.0-beta.12", + "xterm-addon-serialize": "0.7.0-beta.13", "xterm-addon-unicode11": "0.4.0-beta.3", - "xterm-addon-webgl": "0.12.0-beta.39", + "xterm-addon-webgl": "0.12.0-beta.41", "xterm-headless": "4.19.0-beta.60", "yauzl": "^2.9.2", "yazl": "^2.4.3" diff --git a/remote/web/package.json b/remote/web/package.json index bb4ed6ee77d..fcc462b5733 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -15,6 +15,6 @@ "xterm": "4.19.0-beta.60", "xterm-addon-search": "0.9.0-beta.39", "xterm-addon-unicode11": "0.4.0-beta.3", - "xterm-addon-webgl": "0.12.0-beta.39" + "xterm-addon-webgl": "0.12.0-beta.41" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index b837505240e..643265cb38c 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -141,10 +141,10 @@ xterm-addon-unicode11@0.4.0-beta.3: resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.4.0-beta.3.tgz#f350184155fafd5ad0d6fbf31d13e6ca7dea1efa" integrity sha512-FryZAVwbUjKTmwXnm1trch/2XO60F5JsDvOkZhzobV1hm10sFLVuZpFyHXiUx7TFeeFsvNP+S77LAtWoeT5z+Q== -xterm-addon-webgl@0.12.0-beta.39: - version "0.12.0-beta.39" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.39.tgz#f5fafd2c4d1ec046ff6b57edf850234948cf630a" - integrity sha512-116gk9KYqbMyCcjedDYhqg3RKl0CI19I1l/+u8JdJb6bucv835Hj60W2it5AAsmvw726FEXwhMoOaY3gs+n1/g== +xterm-addon-webgl@0.12.0-beta.41: + version "0.12.0-beta.41" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.41.tgz#17dbca975b6e9b34526ebc57f4de59ee295da1b6" + integrity sha512-wvQxC5diMYEJEMaILfz+4CWB2GgtzjzNQRNDnK7R7Y9wDI+P4idDlQKgyH0nA93sl9R4zgqlBVha//wuq4vfZg== xterm@4.19.0-beta.60: version "4.19.0-beta.60" diff --git a/remote/yarn.lock b/remote/yarn.lock index b216f82db48..6ee84100308 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -937,20 +937,20 @@ xterm-addon-search@0.9.0-beta.39: resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.39.tgz#e8376e1485ee7d763c07d1a8f1354114f65b3e3e" integrity sha512-h45wkecgfqXXoAUqgNytAfSd6g0xNT6rZy/enVaEU0aes7QoL9pxHUKkCry8PP6hs03Slk0VxQ4AGsbSZGvK/w== -xterm-addon-serialize@0.7.0-beta.12: - version "0.7.0-beta.12" - resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.7.0-beta.12.tgz#4f845d8b1a9f9b7ae3f910455ce8c58b041babc7" - integrity sha512-b4Ug0B/RSJMux+KAcp+PXVqubVyXjN1yCQw1FOkgVYTpmd9AH/X+EcxKml5Lz8DsKmsXqfD9AlV3WpEeT+OtMw== +xterm-addon-serialize@0.7.0-beta.13: + version "0.7.0-beta.13" + resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.7.0-beta.13.tgz#5c859c8657cab7f28405aab1a0715daf54bc7714" + integrity sha512-TYFlm/gds0pOmpzXw7ZWx8Cy48lMaOZZqZgfm5pWU37HPvzfKxXSVdYL1biWpRCH2zCH+3cWmOma8W1pBRr+Eg== xterm-addon-unicode11@0.4.0-beta.3: version "0.4.0-beta.3" resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.4.0-beta.3.tgz#f350184155fafd5ad0d6fbf31d13e6ca7dea1efa" integrity sha512-FryZAVwbUjKTmwXnm1trch/2XO60F5JsDvOkZhzobV1hm10sFLVuZpFyHXiUx7TFeeFsvNP+S77LAtWoeT5z+Q== -xterm-addon-webgl@0.12.0-beta.39: - version "0.12.0-beta.39" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.39.tgz#f5fafd2c4d1ec046ff6b57edf850234948cf630a" - integrity sha512-116gk9KYqbMyCcjedDYhqg3RKl0CI19I1l/+u8JdJb6bucv835Hj60W2it5AAsmvw726FEXwhMoOaY3gs+n1/g== +xterm-addon-webgl@0.12.0-beta.41: + version "0.12.0-beta.41" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.41.tgz#17dbca975b6e9b34526ebc57f4de59ee295da1b6" + integrity sha512-wvQxC5diMYEJEMaILfz+4CWB2GgtzjzNQRNDnK7R7Y9wDI+P4idDlQKgyH0nA93sl9R4zgqlBVha//wuq4vfZg== xterm-headless@4.19.0-beta.60: version "4.19.0-beta.60" diff --git a/yarn.lock b/yarn.lock index ab45aaaeb4b..fadbc67e755 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12261,20 +12261,20 @@ xterm-addon-search@0.9.0-beta.39: resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.39.tgz#e8376e1485ee7d763c07d1a8f1354114f65b3e3e" integrity sha512-h45wkecgfqXXoAUqgNytAfSd6g0xNT6rZy/enVaEU0aes7QoL9pxHUKkCry8PP6hs03Slk0VxQ4AGsbSZGvK/w== -xterm-addon-serialize@0.7.0-beta.12: - version "0.7.0-beta.12" - resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.7.0-beta.12.tgz#4f845d8b1a9f9b7ae3f910455ce8c58b041babc7" - integrity sha512-b4Ug0B/RSJMux+KAcp+PXVqubVyXjN1yCQw1FOkgVYTpmd9AH/X+EcxKml5Lz8DsKmsXqfD9AlV3WpEeT+OtMw== +xterm-addon-serialize@0.7.0-beta.13: + version "0.7.0-beta.13" + resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.7.0-beta.13.tgz#5c859c8657cab7f28405aab1a0715daf54bc7714" + integrity sha512-TYFlm/gds0pOmpzXw7ZWx8Cy48lMaOZZqZgfm5pWU37HPvzfKxXSVdYL1biWpRCH2zCH+3cWmOma8W1pBRr+Eg== xterm-addon-unicode11@0.4.0-beta.3: version "0.4.0-beta.3" resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.4.0-beta.3.tgz#f350184155fafd5ad0d6fbf31d13e6ca7dea1efa" integrity sha512-FryZAVwbUjKTmwXnm1trch/2XO60F5JsDvOkZhzobV1hm10sFLVuZpFyHXiUx7TFeeFsvNP+S77LAtWoeT5z+Q== -xterm-addon-webgl@0.12.0-beta.39: - version "0.12.0-beta.39" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.39.tgz#f5fafd2c4d1ec046ff6b57edf850234948cf630a" - integrity sha512-116gk9KYqbMyCcjedDYhqg3RKl0CI19I1l/+u8JdJb6bucv835Hj60W2it5AAsmvw726FEXwhMoOaY3gs+n1/g== +xterm-addon-webgl@0.12.0-beta.41: + version "0.12.0-beta.41" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.41.tgz#17dbca975b6e9b34526ebc57f4de59ee295da1b6" + integrity sha512-wvQxC5diMYEJEMaILfz+4CWB2GgtzjzNQRNDnK7R7Y9wDI+P4idDlQKgyH0nA93sl9R4zgqlBVha//wuq4vfZg== xterm-headless@4.19.0-beta.60: version "4.19.0-beta.60" From 8e29aa44f7f25278ef95bda2e7d50122ac5be3fe Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 17 Jun 2022 08:43:31 -0700 Subject: [PATCH 156/175] Use new method for settings in terminal split cwd smoke test I doubt this will fix the flake, but it's the right way to do things now. Part of #152451 --- test/smoke/src/areas/terminal/terminal-splitCwd.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/smoke/src/areas/terminal/terminal-splitCwd.test.ts b/test/smoke/src/areas/terminal/terminal-splitCwd.test.ts index 8f1eb1eed71..750c9f090b4 100644 --- a/test/smoke/src/areas/terminal/terminal-splitCwd.test.ts +++ b/test/smoke/src/areas/terminal/terminal-splitCwd.test.ts @@ -15,8 +15,9 @@ export function setup() { const app = this.app as Application; terminal = app.workbench.terminal; settingsEditor = app.workbench.settingsEditor; - await settingsEditor.addUserSetting('terminal.integrated.splitCwd', '"inherited"'); - await setTerminalTestSettings(app); + await setTerminalTestSettings(app, [ + ['terminal.integrated.splitCwd', '"inherited"'] + ]); }); after(async function () { From 012a409a510d4031a05b15de1087673978d4495e Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 17 Jun 2022 10:04:23 -0700 Subject: [PATCH 157/175] Make sure _serializer is set before calling setTitle --- src/vs/platform/terminal/node/ptyService.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index d387f795c72..9cf88cb6b42 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -513,9 +513,6 @@ export class PersistentTerminalProcess extends Disposable { fixedDimensions?: IFixedTerminalDimensions ) { super(); - if (name) { - this.setTitle(name, TitleEventSource.Api); - } this._logService.trace('persistentTerminalProcess#ctor', _persistentProcessId, arguments); this._wasRevived = reviveBuffer !== undefined; this._serializer = new XtermSerializer( @@ -524,9 +521,12 @@ export class PersistentTerminalProcess extends Disposable { reconnectConstants.scrollback, unicodeVersion, reviveBuffer, - rawReviveBuffer, + shouldPersistTerminal ? rawReviveBuffer : undefined, this._logService ); + if (name) { + this.setTitle(name, TitleEventSource.Api); + } this._fixedDimensions = fixedDimensions; this._orphanQuestionBarrier = null; this._orphanQuestionReplyTime = 0; From 3c65f5295d42145e17b63877d60f0d12a9c53eb2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 17 Jun 2022 19:15:38 +0200 Subject: [PATCH 158/175] api - polish USB command (#152424) --- src/vs/base/browser/usb.ts | 52 +++++++++++++++++++ .../browser/actions/workspaceCommands.ts | 41 --------------- src/vs/workbench/browser/window.ts | 16 +++++- 3 files changed, 67 insertions(+), 42 deletions(-) create mode 100644 src/vs/base/browser/usb.ts diff --git a/src/vs/base/browser/usb.ts b/src/vs/base/browser/usb.ts new file mode 100644 index 00000000000..63b5d40f3e0 --- /dev/null +++ b/src/vs/base/browser/usb.ts @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// https://wicg.github.io/webusb/ + +export interface UsbDeviceData { + readonly deviceClass: number; + readonly deviceProtocol: number; + readonly deviceSubclass: number; + readonly deviceVersionMajor: number; + readonly deviceVersionMinor: number; + readonly deviceVersionSubminor: number; + readonly manufacturerName?: string; + readonly productId: number; + readonly productName?: string; + readonly serialNumber?: string; + readonly usbVersionMajor: number; + readonly usbVersionMinor: number; + readonly usbVersionSubminor: number; + readonly vendorId: number; +} + +export async function requestUsb(options?: { filters?: unknown[] }): Promise { + const usb = (navigator as any).usb; + if (!usb) { + return undefined; + } + + const device = await usb.requestDevice({ filters: options?.filters ?? [] }); + if (!device) { + return undefined; + } + + return { + deviceClass: device.deviceClass, + deviceProtocol: device.deviceProtocol, + deviceSubclass: device.deviceSubclass, + deviceVersionMajor: device.deviceVersionMajor, + deviceVersionMinor: device.deviceVersionMinor, + deviceVersionSubminor: device.deviceVersionSubminor, + manufacturerName: device.manufacturerName, + productId: device.productId, + productName: device.productName, + serialNumber: device.serialNumber, + usbVersionMajor: device.usbVersionMajor, + usbVersionMinor: device.usbVersionMinor, + usbVersionSubminor: device.usbVersionSubminor, + vendorId: device.vendorId, + }; +} diff --git a/src/vs/workbench/browser/actions/workspaceCommands.ts b/src/vs/workbench/browser/actions/workspaceCommands.ts index f293f39d156..16796a686b6 100644 --- a/src/vs/workbench/browser/actions/workspaceCommands.ts +++ b/src/vs/workbench/browser/actions/workspaceCommands.ts @@ -24,7 +24,6 @@ import { IOpenEmptyWindowOptions, IOpenWindowOptions, IWindowOpenable } from 'vs import { IRecent, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { ILocalizedString } from 'vs/platform/action/common/action'; -import { isWeb } from 'vs/base/common/platform'; export const ADD_ROOT_FOLDER_COMMAND_ID = 'addRootFolder'; export const ADD_ROOT_FOLDER_LABEL: ILocalizedString = { value: localize('addFolderToWorkspace', "Add Folder to Workspace..."), original: 'Add Folder to Workspace...' }; @@ -308,43 +307,3 @@ CommandsRegistry.registerCommand('_workbench.getRecentlyOpened', async function return workspacesService.getRecentlyOpened(); }); -if (isWeb) { - interface UsbDeviceData { - readonly deviceClass: number; - readonly deviceProtocol: number; - readonly deviceSubclass: number; - readonly deviceVersionMajor: number; - readonly deviceVersionMinor: number; - readonly deviceVersionSubminor: number; - readonly manufacturerName?: string; - readonly productId: number; - readonly productName?: string; - readonly serialNumber?: string; - readonly usbVersionMajor: number; - readonly usbVersionMinor: number; - readonly usbVersionSubminor: number; - readonly vendorId: number; - } - CommandsRegistry.registerCommand('workbench.experimental.requestUsbDevice', async (_accessor: ServicesAccessor, options?: { filters?: unknown[] }): Promise => { - const device = await (navigator as any).usb.requestDevice({ filters: options?.filters ?? [] }); - if (!device) { - return undefined; - } - return { - deviceClass: device.deviceClass, - deviceProtocol: device.deviceProtocol, - deviceSubclass: device.deviceSubclass, - deviceVersionMajor: device.deviceVersionMajor, - deviceVersionMinor: device.deviceVersionMinor, - deviceVersionSubminor: device.deviceVersionSubminor, - manufacturerName: device.manufacturerName, - productId: device.productId, - productName: device.productName, - serialNumber: device.serialNumber, - usbVersionMajor: device.usbVersionMajor, - usbVersionMinor: device.usbVersionMinor, - usbVersionSubminor: device.usbVersionSubminor, - vendorId: device.vendorId, - }; - }); -} diff --git a/src/vs/workbench/browser/window.ts b/src/vs/workbench/browser/window.ts index 7d15b8bf58f..3c991cc3972 100644 --- a/src/vs/workbench/browser/window.ts +++ b/src/vs/workbench/browser/window.ts @@ -6,6 +6,7 @@ import { isSafari, setFullscreen } from 'vs/base/browser/browser'; import { addDisposableListener, addDisposableThrottledListener, detectFullscreen, EventHelper, EventType, windowOpenNoOpener, windowOpenPopup, windowOpenWithSuccess } from 'vs/base/browser/dom'; import { DomEmitter } from 'vs/base/browser/event'; +import { requestUsb, UsbDeviceData } from 'vs/base/browser/usb'; import { timeout } from 'vs/base/common/async'; import { Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -14,8 +15,10 @@ import { isIOS, isMacintosh } from 'vs/base/common/platform'; import Severity from 'vs/base/common/severity'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { registerWindowDriver } from 'vs/platform/driver/browser/driver'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; import { IOpenerService, matchesScheme } from 'vs/platform/opener/common/opener'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -120,6 +123,9 @@ export class BrowserWindow extends Disposable { // Label formatting this.registerLabelFormatters(); + // Commands + this.registerCommands(); + // Smoke Test Driver this.setupDriver(); } @@ -227,7 +233,7 @@ export class BrowserWindow extends Disposable { }); } - private registerLabelFormatters() { + private registerLabelFormatters(): void { this._register(this.labelService.registerFormatter({ scheme: Schemas.vscodeUserData, priority: true, @@ -237,4 +243,12 @@ export class BrowserWindow extends Disposable { } })); } + + private registerCommands(): void { + + // Allow extensions to request USB devices in Web + CommandsRegistry.registerCommand('workbench.experimental.requestUsbDevice', async (_accessor: ServicesAccessor, options?: { filters?: unknown[] }): Promise => { + return requestUsb(options); + }); + } } From 5947c2a93cb32da9d6f2c39f8c1adcf7699a0ce1 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Fri, 17 Jun 2022 13:18:36 -0400 Subject: [PATCH 159/175] Bump distro (#152483) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ef009c0df53..f06aff9e674 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.69.0", - "distro": "c58f78790c58ab690d170012ad340f84cff8b438", + "distro": "67db0559282b6e725248321639550845abfbe291", "author": { "name": "Microsoft Corporation" }, From dea813ff7c1b317d66b3937bbad74bfc711980d9 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 17 Jun 2022 11:20:02 -0700 Subject: [PATCH 160/175] Add table of contents provider abstraction (#152504) We currently re-compute the same table of contents for markdown files multiple times. This is because multiple language features all need table of contents With this change, we introduce a new `TableOfContentsProvider` which maintains a cache of the table of contents per file. This provider is then passed into every caller that needs a toc --- .../src/extension.ts | 15 ++-- .../src/languageFeatures/diagnostics.ts | 19 ++--- .../documentSymbolProvider.ts | 13 ++-- .../src/languageFeatures/foldingProvider.ts | 12 ++-- .../src/languageFeatures/references.ts | 7 +- .../src/languageFeatures/smartSelect.ts | 10 +-- .../src/tableOfContents.ts | 25 ++++++- .../src/test/definitionProvider.test.ts | 3 +- .../src/test/diagnostic.test.ts | 9 ++- .../src/test/documentSymbolProvider.test.ts | 8 ++- .../src/test/fileReferences.test.ts | 5 +- .../src/test/foldingProvider.test.ts | 6 +- .../src/test/references.test.ts | 5 +- .../src/test/rename.test.ts | 5 +- .../src/test/smartSelect.test.ts | 6 +- .../src/test/workspaceSymbolProvider.test.ts | 71 +++++++++---------- 16 files changed, 133 insertions(+), 86 deletions(-) diff --git a/extensions/markdown-language-features/src/extension.ts b/extensions/markdown-language-features/src/extension.ts index be3a7858fe8..a97caf760a5 100644 --- a/extensions/markdown-language-features/src/extension.ts +++ b/extensions/markdown-language-features/src/extension.ts @@ -26,6 +26,7 @@ import { MarkdownContentProvider } from './preview/previewContentProvider'; import { MarkdownPreviewManager } from './preview/previewManager'; import { ContentSecurityPolicyArbiter, ExtensionContentSecurityPolicyArbiter, PreviewSecuritySelector } from './preview/security'; import { githubSlugifier } from './slugify'; +import { MdTableOfContentsProvider } from './tableOfContents'; import { loadDefaultTelemetryReporter, TelemetryReporter } from './telemetryReporter'; import { VsCodeMdWorkspaceContents } from './workspaceContents'; @@ -64,27 +65,29 @@ function registerMarkdownLanguageFeatures( const workspaceContents = new VsCodeMdWorkspaceContents(); const linkProvider = new MdLinkProvider(engine, workspaceContents); - const referencesProvider = new MdReferencesProvider(engine, workspaceContents); - const symbolProvider = new MdDocumentSymbolProvider(engine); + const tocProvider = new MdTableOfContentsProvider(engine, workspaceContents); + const referencesProvider = new MdReferencesProvider(engine, workspaceContents, tocProvider); + const symbolProvider = new MdDocumentSymbolProvider(tocProvider); return vscode.Disposable.from( workspaceContents, linkProvider, referencesProvider, + tocProvider, // Language features registerDefinitionSupport(selector, referencesProvider), - registerDiagnosticSupport(selector, engine, workspaceContents, linkProvider, commandManager, referencesProvider), + registerDiagnosticSupport(selector, engine, workspaceContents, linkProvider, commandManager, referencesProvider, tocProvider), registerDocumentLinkSupport(selector, linkProvider), - registerDocumentSymbolSupport(selector, engine), + registerDocumentSymbolSupport(selector, tocProvider), registerDropIntoEditorSupport(selector), registerFindFileReferenceSupport(commandManager, referencesProvider), - registerFoldingSupport(selector, engine), + registerFoldingSupport(selector, engine, tocProvider), registerPasteSupport(selector), registerPathCompletionSupport(selector, engine, linkProvider), registerReferencesSupport(selector, referencesProvider), registerRenameSupport(selector, workspaceContents, referencesProvider, engine.slugifier), - registerSmartSelectSupport(selector, engine), + registerSmartSelectSupport(selector, engine, tocProvider), registerWorkspaceSymbolSupport(workspaceContents, symbolProvider), ); } diff --git a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts index 8a1dbe1c619..cea19b2d435 100644 --- a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts @@ -8,16 +8,16 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import { CommandManager } from '../commandManager'; import { MarkdownEngine } from '../markdownEngine'; -import { TableOfContents } from '../tableOfContents'; +import { MdTableOfContentsProvider } from '../tableOfContents'; +import { MdTableOfContentsWatcher } from '../test/tableOfContentsWatcher'; import { Delayer } from '../util/async'; import { noopToken } from '../util/cancellation'; import { Disposable } from '../util/dispose'; import { isMarkdownFile } from '../util/file'; import { Limiter } from '../util/limiter'; import { ResourceMap } from '../util/resourceMap'; -import { MdTableOfContentsWatcher } from '../test/tableOfContentsWatcher'; import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; -import { InternalHref, MdLink, MdLinkSource, MdLinkProvider, LinkDefinitionSet } from './documentLinkProvider'; +import { InternalHref, LinkDefinitionSet, MdLink, MdLinkProvider, MdLinkSource } from './documentLinkProvider'; import { MdReferencesProvider, tryFindMdDocumentForLink } from './references'; const localize = nls.loadMessageBundle(); @@ -448,9 +448,9 @@ class FileLinkMap { export class DiagnosticComputer { constructor( - private readonly engine: MarkdownEngine, private readonly workspaceContents: MdWorkspaceContents, private readonly linkProvider: MdLinkProvider, + private readonly tocProvider: MdTableOfContentsProvider, ) { } public async getDiagnostics(doc: SkinnyTextDocument, options: DiagnosticOptions, token: vscode.CancellationToken): Promise<{ readonly diagnostics: vscode.Diagnostic[]; readonly links: readonly MdLink[] }> { @@ -475,7 +475,7 @@ export class DiagnosticComputer { return []; } - const toc = await TableOfContents.create(this.engine, doc); + const toc = await this.tocProvider.get(doc.uri); if (token.isCancellationRequested) { return []; } @@ -552,7 +552,7 @@ export class DiagnosticComputer { // Validate each of the links to headers in the file const fragmentLinks = links.filter(x => x.fragment); if (fragmentLinks.length) { - const toc = await TableOfContents.create(this.engine, hrefDoc); + const toc = await this.tocProvider.get(hrefDoc.uri); for (const link of fragmentLinks) { if (!toc.lookup(link.fragment) && !this.isIgnoredLink(options, link.source.pathText) && !this.isIgnoredLink(options, link.source.text)) { const msg = localize('invalidLinkToHeaderInOtherFile', 'Header does not exist in file: {0}', link.fragment); @@ -625,16 +625,17 @@ export function registerDiagnosticSupport( workspaceContents: MdWorkspaceContents, linkProvider: MdLinkProvider, commandManager: CommandManager, - referenceComputer: MdReferencesProvider, + referenceProvider: MdReferencesProvider, + tocProvider: MdTableOfContentsProvider, ): vscode.Disposable { const configuration = new VSCodeDiagnosticConfiguration(); const manager = new DiagnosticManager( engine, workspaceContents, - new DiagnosticComputer(engine, workspaceContents, linkProvider), + new DiagnosticComputer(workspaceContents, linkProvider, tocProvider), configuration, new DiagnosticCollectionReporter(), - referenceComputer); + referenceProvider); return vscode.Disposable.from( configuration, manager, diff --git a/extensions/markdown-language-features/src/languageFeatures/documentSymbolProvider.ts b/extensions/markdown-language-features/src/languageFeatures/documentSymbolProvider.ts index ba89c2d1046..e72c92f3554 100644 --- a/extensions/markdown-language-features/src/languageFeatures/documentSymbolProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/documentSymbolProvider.ts @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { MarkdownEngine } from '../markdownEngine'; -import { TableOfContents, TocEntry } from '../tableOfContents'; +import { MdTableOfContentsProvider, TocEntry } from '../tableOfContents'; import { SkinnyTextDocument } from '../workspaceContents'; interface MarkdownSymbol { @@ -17,16 +16,16 @@ interface MarkdownSymbol { export class MdDocumentSymbolProvider implements vscode.DocumentSymbolProvider { constructor( - private readonly engine: MarkdownEngine + private readonly tocProvider: MdTableOfContentsProvider, ) { } public async provideDocumentSymbolInformation(document: SkinnyTextDocument): Promise { - const toc = await TableOfContents.create(this.engine, document); + const toc = await this.tocProvider.get(document.uri); return toc.entries.map(entry => this.toSymbolInformation(entry)); } public async provideDocumentSymbols(document: SkinnyTextDocument): Promise { - const toc = await TableOfContents.create(this.engine, document); + const toc = await this.tocProvider.get(document.uri); const root: MarkdownSymbol = { level: -Infinity, children: [], @@ -77,7 +76,7 @@ export class MdDocumentSymbolProvider implements vscode.DocumentSymbolProvider { export function registerDocumentSymbolSupport( selector: vscode.DocumentSelector, - engine: MarkdownEngine, + tocProvider: MdTableOfContentsProvider, ): vscode.Disposable { - return vscode.languages.registerDocumentSymbolProvider(selector, new MdDocumentSymbolProvider(engine)); + return vscode.languages.registerDocumentSymbolProvider(selector, new MdDocumentSymbolProvider(tocProvider)); } diff --git a/extensions/markdown-language-features/src/languageFeatures/foldingProvider.ts b/extensions/markdown-language-features/src/languageFeatures/foldingProvider.ts index 13675908722..a3b0fb44f1d 100644 --- a/extensions/markdown-language-features/src/languageFeatures/foldingProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/foldingProvider.ts @@ -6,7 +6,7 @@ import Token = require('markdown-it/lib/token'); import * as vscode from 'vscode'; import { MarkdownEngine } from '../markdownEngine'; -import { TableOfContents } from '../tableOfContents'; +import { MdTableOfContentsProvider } from '../tableOfContents'; import { SkinnyTextDocument } from '../workspaceContents'; const rangeLimit = 5000; @@ -18,7 +18,8 @@ interface MarkdownItTokenWithMap extends Token { export class MdFoldingProvider implements vscode.FoldingRangeProvider { constructor( - private readonly engine: MarkdownEngine + private readonly engine: MarkdownEngine, + private readonly tocProvide: MdTableOfContentsProvider, ) { } public async provideFoldingRanges( @@ -54,8 +55,8 @@ export class MdFoldingProvider implements vscode.FoldingRangeProvider { .filter((region: vscode.FoldingRange | null): region is vscode.FoldingRange => !!region); } - private async getHeaderFoldingRanges(document: SkinnyTextDocument) { - const toc = await TableOfContents.create(this.engine, document); + private async getHeaderFoldingRanges(document: SkinnyTextDocument): Promise { + const toc = await this.tocProvide.get(document.uri); return toc.entries.map(entry => { let endLine = entry.sectionLocation.range.end.line; if (document.lineAt(endLine).isEmptyOrWhitespace && endLine >= entry.line + 1) { @@ -115,6 +116,7 @@ const isFoldableToken = (token: Token): token is MarkdownItTokenWithMap => { export function registerFoldingSupport( selector: vscode.DocumentSelector, engine: MarkdownEngine, + tocProvider: MdTableOfContentsProvider, ): vscode.Disposable { - return vscode.languages.registerFoldingRangeProvider(selector, new MdFoldingProvider(engine)); + return vscode.languages.registerFoldingRangeProvider(selector, new MdFoldingProvider(engine, tocProvider)); } diff --git a/extensions/markdown-language-features/src/languageFeatures/references.ts b/extensions/markdown-language-features/src/languageFeatures/references.ts index d386204a333..7e99b34e569 100644 --- a/extensions/markdown-language-features/src/languageFeatures/references.ts +++ b/extensions/markdown-language-features/src/languageFeatures/references.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; import * as uri from 'vscode-uri'; import { MarkdownEngine } from '../markdownEngine'; -import { TableOfContents, TocEntry } from '../tableOfContents'; +import { MdTableOfContentsProvider, TocEntry } from '../tableOfContents'; import { noopToken } from '../util/cancellation'; import { Disposable } from '../util/dispose'; import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; @@ -69,6 +69,7 @@ export class MdReferencesProvider extends Disposable { public constructor( private readonly engine: MarkdownEngine, private readonly workspaceContents: MdWorkspaceContents, + private readonly tocProvider: MdTableOfContentsProvider, ) { super(); @@ -77,7 +78,7 @@ export class MdReferencesProvider extends Disposable { } public async getReferencesAtPosition(document: SkinnyTextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { - const toc = await TableOfContents.create(this.engine, document); + const toc = await this.tocProvider.get(document.uri); if (token.isCancellationRequested) { return []; } @@ -184,7 +185,7 @@ export class MdReferencesProvider extends Disposable { const references: MdReference[] = []; if (targetDoc && sourceLink.href.fragment && sourceLink.source.fragmentRange?.contains(triggerPosition)) { - const toc = await TableOfContents.create(this.engine, targetDoc); + const toc = await this.tocProvider.get(targetDoc.uri); const entry = toc.lookup(sourceLink.href.fragment); if (entry) { references.push({ diff --git a/extensions/markdown-language-features/src/languageFeatures/smartSelect.ts b/extensions/markdown-language-features/src/languageFeatures/smartSelect.ts index 09cdd4acfce..fd494a0f8fe 100644 --- a/extensions/markdown-language-features/src/languageFeatures/smartSelect.ts +++ b/extensions/markdown-language-features/src/languageFeatures/smartSelect.ts @@ -5,7 +5,7 @@ import Token = require('markdown-it/lib/token'); import * as vscode from 'vscode'; import { MarkdownEngine } from '../markdownEngine'; -import { TableOfContents, TocEntry } from '../tableOfContents'; +import { MdTableOfContentsProvider, TocEntry } from '../tableOfContents'; import { SkinnyTextDocument } from '../workspaceContents'; interface MarkdownItTokenWithMap extends Token { @@ -15,7 +15,8 @@ interface MarkdownItTokenWithMap extends Token { export class MdSmartSelect implements vscode.SelectionRangeProvider { constructor( - private readonly engine: MarkdownEngine + private readonly engine: MarkdownEngine, + private readonly tocProvider: MdTableOfContentsProvider, ) { } public async provideSelectionRanges(document: SkinnyTextDocument, positions: vscode.Position[], _token: vscode.CancellationToken): Promise { @@ -54,7 +55,7 @@ export class MdSmartSelect implements vscode.SelectionRangeProvider { } private async getHeaderSelectionRange(document: SkinnyTextDocument, position: vscode.Position): Promise { - const toc = await TableOfContents.create(this.engine, document); + const toc = await this.tocProvider.get(document.uri); const headerInfo = getHeadersForPosition(toc.entries, position); @@ -253,6 +254,7 @@ function getFirstChildHeader(document: SkinnyTextDocument, header?: TocEntry, to export function registerSmartSelectSupport( selector: vscode.DocumentSelector, engine: MarkdownEngine, + tocProvider: MdTableOfContentsProvider, ): vscode.Disposable { - return vscode.languages.registerSelectionRangeProvider(selector, new MdSmartSelect(engine)); + return vscode.languages.registerSelectionRangeProvider(selector, new MdSmartSelect(engine, tocProvider)); } diff --git a/extensions/markdown-language-features/src/tableOfContents.ts b/extensions/markdown-language-features/src/tableOfContents.ts index 33a65d997b5..6935763b3ec 100644 --- a/extensions/markdown-language-features/src/tableOfContents.ts +++ b/extensions/markdown-language-features/src/tableOfContents.ts @@ -4,10 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import { MdDocumentInfoCache } from './languageFeatures/workspaceCache'; import { MarkdownEngine } from './markdownEngine'; import { githubSlugifier, Slug } from './slugify'; +import { Disposable } from './util/dispose'; import { isMarkdownFile } from './util/file'; -import { SkinnyTextDocument } from './workspaceContents'; +import { MdWorkspaceContents, SkinnyTextDocument } from './workspaceContents'; export interface TocEntry { readonly slug: Slug; @@ -161,6 +163,8 @@ export class TableOfContents { return header.replace(/^\s*#+\s*(.*?)(\s+#+)?$/, (_, word) => word.trim()); } + public static readonly empty = new TableOfContents([]); + private constructor( public readonly entries: readonly TocEntry[], ) { } @@ -170,3 +174,22 @@ export class TableOfContents { return this.entries.find(entry => entry.slug.equals(slug)); } } + +export class MdTableOfContentsProvider extends Disposable { + + private readonly _cache: MdDocumentInfoCache; + + constructor( + engine: MarkdownEngine, + workspaceContents: MdWorkspaceContents, + ) { + super(); + this._cache = this._register(new MdDocumentInfoCache(workspaceContents, doc => { + return TableOfContents.create(engine, doc); + })); + } + + public async get(resource: vscode.Uri): Promise { + return (await this._cache.get(resource)) ?? TableOfContents.empty; + } +} diff --git a/extensions/markdown-language-features/src/test/definitionProvider.test.ts b/extensions/markdown-language-features/src/test/definitionProvider.test.ts index 92310565793..9ff5d65c44a 100644 --- a/extensions/markdown-language-features/src/test/definitionProvider.test.ts +++ b/extensions/markdown-language-features/src/test/definitionProvider.test.ts @@ -8,6 +8,7 @@ import 'mocha'; import * as vscode from 'vscode'; import { MdDefinitionProvider } from '../languageFeatures/definitionProvider'; import { MdReferencesProvider } from '../languageFeatures/references'; +import { MdTableOfContentsProvider } from '../tableOfContents'; import { noopToken } from '../util/cancellation'; import { InMemoryDocument } from '../util/inMemoryDocument'; import { MdWorkspaceContents } from '../workspaceContents'; @@ -18,7 +19,7 @@ import { joinLines, workspacePath } from './util'; function getDefinition(doc: InMemoryDocument, pos: vscode.Position, workspace: MdWorkspaceContents) { const engine = createNewMarkdownEngine(); - const referencesProvider = new MdReferencesProvider(engine, workspace); + const referencesProvider = new MdReferencesProvider(engine, workspace, new MdTableOfContentsProvider(engine, workspace)); const provider = new MdDefinitionProvider(referencesProvider); return provider.provideDefinition(doc, pos, noopToken); } diff --git a/extensions/markdown-language-features/src/test/diagnostic.test.ts b/extensions/markdown-language-features/src/test/diagnostic.test.ts index e677d8ee823..199c1d35ceb 100644 --- a/extensions/markdown-language-features/src/test/diagnostic.test.ts +++ b/extensions/markdown-language-features/src/test/diagnostic.test.ts @@ -9,6 +9,7 @@ import * as vscode from 'vscode'; import { DiagnosticCollectionReporter, DiagnosticComputer, DiagnosticConfiguration, DiagnosticLevel, DiagnosticManager, DiagnosticOptions, DiagnosticReporter } from '../languageFeatures/diagnostics'; import { MdLinkProvider } from '../languageFeatures/documentLinkProvider'; import { MdReferencesProvider } from '../languageFeatures/references'; +import { MdTableOfContentsProvider } from '../tableOfContents'; import { noopToken } from '../util/cancellation'; import { disposeAll } from '../util/dispose'; import { InMemoryDocument } from '../util/inMemoryDocument'; @@ -30,7 +31,8 @@ const defaultDiagnosticsOptions = Object.freeze({ async function getComputedDiagnostics(doc: InMemoryDocument, workspace: MdWorkspaceContents, options: Partial = {}): Promise { const engine = createNewMarkdownEngine(); const linkProvider = new MdLinkProvider(engine, workspace); - const computer = new DiagnosticComputer(engine, workspace, linkProvider); + const tocProvider = new MdTableOfContentsProvider(engine, workspace); + const computer = new DiagnosticComputer(workspace, linkProvider, tocProvider); return ( await computer.getDiagnostics(doc, { ...defaultDiagnosticsOptions, ...options, }, noopToken) ).diagnostics; @@ -430,11 +432,12 @@ suite('Markdown: Diagnostics manager', () => { ) { const engine = createNewMarkdownEngine(); const linkProvider = new MdLinkProvider(engine, workspace); - const referencesProvider = new MdReferencesProvider(engine, workspace); + const tocProvider = new MdTableOfContentsProvider(engine, workspace); + const referencesProvider = new MdReferencesProvider(engine, workspace, tocProvider); const manager = new DiagnosticManager( engine, workspace, - new DiagnosticComputer(engine, workspace, linkProvider), + new DiagnosticComputer(workspace, linkProvider, tocProvider), configuration, reporter, referencesProvider, diff --git a/extensions/markdown-language-features/src/test/documentSymbolProvider.test.ts b/extensions/markdown-language-features/src/test/documentSymbolProvider.test.ts index 1d09a441c08..aef737945d3 100644 --- a/extensions/markdown-language-features/src/test/documentSymbolProvider.test.ts +++ b/extensions/markdown-language-features/src/test/documentSymbolProvider.test.ts @@ -6,14 +6,18 @@ import * as assert from 'assert'; import 'mocha'; import { MdDocumentSymbolProvider } from '../languageFeatures/documentSymbolProvider'; -import { createNewMarkdownEngine } from './engine'; +import { MdTableOfContentsProvider } from '../tableOfContents'; import { InMemoryDocument } from '../util/inMemoryDocument'; +import { createNewMarkdownEngine } from './engine'; +import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; import { workspacePath } from './util'; function getSymbolsForFile(fileContents: string) { const doc = new InMemoryDocument(workspacePath('test.md'), fileContents); - const provider = new MdDocumentSymbolProvider(createNewMarkdownEngine()); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc]); + const engine = createNewMarkdownEngine(); + const provider = new MdDocumentSymbolProvider(new MdTableOfContentsProvider(engine, workspace)); return provider.provideDocumentSymbols(doc); } diff --git a/extensions/markdown-language-features/src/test/fileReferences.test.ts b/extensions/markdown-language-features/src/test/fileReferences.test.ts index 2b936b1caa9..2a53245534e 100644 --- a/extensions/markdown-language-features/src/test/fileReferences.test.ts +++ b/extensions/markdown-language-features/src/test/fileReferences.test.ts @@ -7,6 +7,7 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; import { MdReference, MdReferencesProvider } from '../languageFeatures/references'; +import { MdTableOfContentsProvider } from '../tableOfContents'; import { noopToken } from '../util/cancellation'; import { InMemoryDocument } from '../util/inMemoryDocument'; import { MdWorkspaceContents } from '../workspaceContents'; @@ -15,9 +16,9 @@ import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; import { joinLines, workspacePath } from './util'; -function getFileReferences(resource: vscode.Uri, workspaceContents: MdWorkspaceContents) { +function getFileReferences(resource: vscode.Uri, workspace: MdWorkspaceContents) { const engine = createNewMarkdownEngine(); - const computer = new MdReferencesProvider(engine, workspaceContents); + const computer = new MdReferencesProvider(engine, workspace, new MdTableOfContentsProvider(engine, workspace)); return computer.getAllReferencesToFile(resource, noopToken); } diff --git a/extensions/markdown-language-features/src/test/foldingProvider.test.ts b/extensions/markdown-language-features/src/test/foldingProvider.test.ts index bfb78f8a77b..34603af0344 100644 --- a/extensions/markdown-language-features/src/test/foldingProvider.test.ts +++ b/extensions/markdown-language-features/src/test/foldingProvider.test.ts @@ -7,8 +7,10 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; import { MdFoldingProvider } from '../languageFeatures/foldingProvider'; +import { MdTableOfContentsProvider } from '../tableOfContents'; import { InMemoryDocument } from '../util/inMemoryDocument'; import { createNewMarkdownEngine } from './engine'; +import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; import { joinLines } from './util'; const testFileName = vscode.Uri.file('test.md'); @@ -218,6 +220,8 @@ suite('markdown.FoldingProvider', () => { async function getFoldsForDocument(contents: string) { const doc = new InMemoryDocument(testFileName, contents); - const provider = new MdFoldingProvider(createNewMarkdownEngine()); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc]); + const engine = createNewMarkdownEngine(); + const provider = new MdFoldingProvider(engine, new MdTableOfContentsProvider(engine, workspace)); return await provider.provideFoldingRanges(doc, {}, new vscode.CancellationTokenSource().token); } diff --git a/extensions/markdown-language-features/src/test/references.test.ts b/extensions/markdown-language-features/src/test/references.test.ts index c9a9e53b3d1..18e942e0076 100644 --- a/extensions/markdown-language-features/src/test/references.test.ts +++ b/extensions/markdown-language-features/src/test/references.test.ts @@ -7,6 +7,7 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; import { MdReferencesProvider, MdVsCodeReferencesProvider } from '../languageFeatures/references'; +import { MdTableOfContentsProvider } from '../tableOfContents'; import { noopToken } from '../util/cancellation'; import { InMemoryDocument } from '../util/inMemoryDocument'; import { MdWorkspaceContents } from '../workspaceContents'; @@ -15,9 +16,9 @@ import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; import { joinLines, workspacePath } from './util'; -function getReferences(doc: InMemoryDocument, pos: vscode.Position, workspaceContents: MdWorkspaceContents) { +function getReferences(doc: InMemoryDocument, pos: vscode.Position, workspace: MdWorkspaceContents) { const engine = createNewMarkdownEngine(); - const computer = new MdReferencesProvider(engine, workspaceContents); + const computer = new MdReferencesProvider(engine, workspace, new MdTableOfContentsProvider(engine, workspace)); const provider = new MdVsCodeReferencesProvider(computer); return provider.provideReferences(doc, pos, { includeDeclaration: true }, noopToken); } diff --git a/extensions/markdown-language-features/src/test/rename.test.ts b/extensions/markdown-language-features/src/test/rename.test.ts index 95c420eb173..d6dece78ea0 100644 --- a/extensions/markdown-language-features/src/test/rename.test.ts +++ b/extensions/markdown-language-features/src/test/rename.test.ts @@ -9,6 +9,7 @@ import * as vscode from 'vscode'; import { MdReferencesProvider } from '../languageFeatures/references'; import { MdVsCodeRenameProvider, MdWorkspaceEdit } from '../languageFeatures/rename'; import { githubSlugifier } from '../slugify'; +import { MdTableOfContentsProvider } from '../tableOfContents'; import { noopToken } from '../util/cancellation'; import { InMemoryDocument } from '../util/inMemoryDocument'; import { MdWorkspaceContents } from '../workspaceContents'; @@ -22,7 +23,7 @@ import { assertRangeEqual, joinLines, workspacePath } from './util'; */ function prepareRename(doc: InMemoryDocument, pos: vscode.Position, workspace: MdWorkspaceContents): Promise { const engine = createNewMarkdownEngine(); - const referenceComputer = new MdReferencesProvider(engine, workspace); + const referenceComputer = new MdReferencesProvider(engine, workspace, new MdTableOfContentsProvider(engine, workspace)); const renameProvider = new MdVsCodeRenameProvider(workspace, referenceComputer, githubSlugifier); return renameProvider.prepareRename(doc, pos, noopToken); } @@ -32,7 +33,7 @@ function prepareRename(doc: InMemoryDocument, pos: vscode.Position, workspace: M */ function getRenameEdits(doc: InMemoryDocument, pos: vscode.Position, newName: string, workspace: MdWorkspaceContents): Promise { const engine = createNewMarkdownEngine(); - const referencesProvider = new MdReferencesProvider(engine, workspace); + const referencesProvider = new MdReferencesProvider(engine, workspace, new MdTableOfContentsProvider(engine, workspace)); const renameProvider = new MdVsCodeRenameProvider(workspace, referencesProvider, githubSlugifier); return renameProvider.provideRenameEditsImpl(doc, pos, newName, noopToken); } diff --git a/extensions/markdown-language-features/src/test/smartSelect.test.ts b/extensions/markdown-language-features/src/test/smartSelect.test.ts index 9d7f60bf481..9298d5972e4 100644 --- a/extensions/markdown-language-features/src/test/smartSelect.test.ts +++ b/extensions/markdown-language-features/src/test/smartSelect.test.ts @@ -9,6 +9,8 @@ import { MdSmartSelect } from '../languageFeatures/smartSelect'; import { createNewMarkdownEngine } from './engine'; import { InMemoryDocument } from '../util/inMemoryDocument'; import { CURSOR, getCursorPositions, joinLines } from './util'; +import { MdTableOfContentsProvider } from '../tableOfContents'; +import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; const testFileName = vscode.Uri.file('test.md'); @@ -720,7 +722,9 @@ function assertLineNumbersEqual(selectionRange: vscode.SelectionRange, startLine function getSelectionRangesForDocument(contents: string, pos?: vscode.Position[]): Promise { const doc = new InMemoryDocument(testFileName, contents); - const provider = new MdSmartSelect(createNewMarkdownEngine()); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc]); + const engine = createNewMarkdownEngine(); + const provider = new MdSmartSelect(engine, new MdTableOfContentsProvider(engine, workspace)); const positions = pos ? pos : getCursorPositions(contents, doc); return provider.provideSelectionRanges(doc, positions, new vscode.CancellationTokenSource().token); } diff --git a/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts b/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts index a059159f613..1302a29ab6f 100644 --- a/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts +++ b/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts @@ -8,29 +8,31 @@ import 'mocha'; import * as vscode from 'vscode'; import { MdDocumentSymbolProvider } from '../languageFeatures/documentSymbolProvider'; import { MdWorkspaceSymbolProvider } from '../languageFeatures/workspaceSymbolProvider'; -import { SkinnyTextDocument } from '../workspaceContents'; -import { createNewMarkdownEngine } from './engine'; +import { MdTableOfContentsProvider } from '../tableOfContents'; import { InMemoryDocument } from '../util/inMemoryDocument'; +import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; +import { createNewMarkdownEngine } from './engine'; import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; +import { workspacePath } from './util'; - -const symbolProvider = new MdDocumentSymbolProvider(createNewMarkdownEngine()); +function getWorkspaceSymbols(workspace: MdWorkspaceContents, query = ''): Promise { + const engine = createNewMarkdownEngine(); + const symbolProvider = new MdDocumentSymbolProvider(new MdTableOfContentsProvider(engine, workspace)); + return new MdWorkspaceSymbolProvider(symbolProvider, workspace).provideWorkspaceSymbols(query); +} suite('markdown.WorkspaceSymbolProvider', () => { test('Should not return anything for empty workspace', async () => { - const provider = new MdWorkspaceSymbolProvider(symbolProvider, new InMemoryWorkspaceMarkdownDocuments([])); - - assert.deepStrictEqual(await provider.provideWorkspaceSymbols(''), []); + const workspace = new InMemoryWorkspaceMarkdownDocuments([]); + assert.deepStrictEqual(await getWorkspaceSymbols(workspace, ''), []); }); test('Should return symbols from workspace with one markdown file', async () => { - const testFileName = vscode.Uri.file('test.md'); + const workspace = new InMemoryWorkspaceMarkdownDocuments([ + new InMemoryDocument(workspacePath('test.md'), `# header1\nabc\n## header2`) + ]); - const provider = new MdWorkspaceSymbolProvider(symbolProvider, new InMemoryWorkspaceMarkdownDocuments([ - new InMemoryDocument(testFileName, `# header1\nabc\n## header2`) - ])); - - const symbols = await provider.provideWorkspaceSymbols(''); + const symbols = await getWorkspaceSymbols(workspace, ''); assert.strictEqual(symbols.length, 2); assert.strictEqual(symbols[0].name, '# header1'); assert.strictEqual(symbols[1].name, '## header2'); @@ -40,64 +42,59 @@ suite('markdown.WorkspaceSymbolProvider', () => { const fileNameCount = 10; const files: SkinnyTextDocument[] = []; for (let i = 0; i < fileNameCount; ++i) { - const testFileName = vscode.Uri.file(`test${i}.md`); + const testFileName = workspacePath(`test${i}.md`); files.push(new InMemoryDocument(testFileName, `# common\nabc\n## header${i}`)); } - const provider = new MdWorkspaceSymbolProvider(symbolProvider, new InMemoryWorkspaceMarkdownDocuments(files)); + const workspace = new InMemoryWorkspaceMarkdownDocuments(files); - const symbols = await provider.provideWorkspaceSymbols(''); + const symbols = await getWorkspaceSymbols(workspace, ''); assert.strictEqual(symbols.length, fileNameCount * 2); }); test('Should update results when markdown file changes symbols', async () => { - const testFileName = vscode.Uri.file('test.md'); - - const workspaceFileProvider = new InMemoryWorkspaceMarkdownDocuments([ + const testFileName = workspacePath('test.md'); + const workspace = new InMemoryWorkspaceMarkdownDocuments([ new InMemoryDocument(testFileName, `# header1`, 1 /* version */) ]); - const provider = new MdWorkspaceSymbolProvider(symbolProvider, workspaceFileProvider); - - assert.strictEqual((await provider.provideWorkspaceSymbols('')).length, 1); + assert.strictEqual((await getWorkspaceSymbols(workspace, '')).length, 1); // Update file - workspaceFileProvider.updateDocument(new InMemoryDocument(testFileName, `# new header\nabc\n## header2`, 2 /* version */)); - const newSymbols = await provider.provideWorkspaceSymbols(''); + workspace.updateDocument(new InMemoryDocument(testFileName, `# new header\nabc\n## header2`, 2 /* version */)); + const newSymbols = await getWorkspaceSymbols(workspace, ''); assert.strictEqual(newSymbols.length, 2); assert.strictEqual(newSymbols[0].name, '# new header'); assert.strictEqual(newSymbols[1].name, '## header2'); }); test('Should remove results when file is deleted', async () => { - const testFileName = vscode.Uri.file('test.md'); + const testFileName = workspacePath('test.md'); - const workspaceFileProvider = new InMemoryWorkspaceMarkdownDocuments([ + const workspace = new InMemoryWorkspaceMarkdownDocuments([ new InMemoryDocument(testFileName, `# header1`) ]); - const provider = new MdWorkspaceSymbolProvider(symbolProvider, workspaceFileProvider); - assert.strictEqual((await provider.provideWorkspaceSymbols('')).length, 1); + assert.strictEqual((await getWorkspaceSymbols(workspace, '')).length, 1); // delete file - workspaceFileProvider.deleteDocument(testFileName); - const newSymbols = await provider.provideWorkspaceSymbols(''); + workspace.deleteDocument(testFileName); + const newSymbols = await getWorkspaceSymbols(workspace, ''); assert.strictEqual(newSymbols.length, 0); }); test('Should update results when markdown file is created', async () => { - const testFileName = vscode.Uri.file('test.md'); + const testFileName = workspacePath('test.md'); - const workspaceFileProvider = new InMemoryWorkspaceMarkdownDocuments([ + const workspace = new InMemoryWorkspaceMarkdownDocuments([ new InMemoryDocument(testFileName, `# header1`) ]); - const provider = new MdWorkspaceSymbolProvider(symbolProvider, workspaceFileProvider); - assert.strictEqual((await provider.provideWorkspaceSymbols('')).length, 1); + assert.strictEqual((await getWorkspaceSymbols(workspace, '')).length, 1); - // Creat file - workspaceFileProvider.createDocument(new InMemoryDocument(vscode.Uri.file('test2.md'), `# new header\nabc\n## header2`)); - const newSymbols = await provider.provideWorkspaceSymbols(''); + // Create file + workspace.createDocument(new InMemoryDocument(workspacePath('test2.md'), `# new header\nabc\n## header2`)); + const newSymbols = await getWorkspaceSymbols(workspace, ''); assert.strictEqual(newSymbols.length, 3); }); }); From 0edb88f3b556ce88f222d5bf71de4d2d958a4476 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Fri, 17 Jun 2022 15:10:04 -0400 Subject: [PATCH 161/175] 1DS appender for the web (#152489) * 1ds web appender * Start testing web --- .eslintrc.json | 6 +- .../platform/telemetry/browser/1dsAppender.ts | 128 ++++++++++++++++++ .../telemetry/browser/telemetryService.ts | 16 ++- 3 files changed, 144 insertions(+), 6 deletions(-) create mode 100644 src/vs/platform/telemetry/browser/1dsAppender.ts diff --git a/.eslintrc.json b/.eslintrc.json index 3e1b349becc..4d49a16fbf0 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -227,8 +227,6 @@ "@vscode/ripgrep", "@vscode/iconv-lite-umd", "applicationinsights", - "@microsoft/1ds-core-js", - "@microsoft/1ds-post-js", "assert", "child_process", "console", @@ -332,7 +330,9 @@ "vs/base/~", "vs/base/parts/*/~", "vs/platform/*/~", - "tas-client-umd" // node module allowed even in /common/ + "tas-client-umd", // node module allowed even in /common/ + "@microsoft/1ds-core-js",// node module allowed even in /common/ + "@microsoft/1ds-post-js" // node module allowed even in /common/ ] }, { diff --git a/src/vs/platform/telemetry/browser/1dsAppender.ts b/src/vs/platform/telemetry/browser/1dsAppender.ts new file mode 100644 index 00000000000..eb91e96acd3 --- /dev/null +++ b/src/vs/platform/telemetry/browser/1dsAppender.ts @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type { AppInsightsCore, IExtendedConfiguration } from '@microsoft/1ds-core-js'; +import type { PostChannel } from '@microsoft/1ds-post-js'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { mixin } from 'vs/base/common/objects'; +import { ITelemetryAppender, validateTelemetryData } from 'vs/platform/telemetry/common/telemetryUtils'; + +const endpointUrl = 'https://mobile.events.data.microsoft.com/OneCollector/1.0'; + +async function getClient(instrumentationKey: string): Promise { + const oneDs = await import('@microsoft/1ds-core-js'); + const postPlugin = await import('@microsoft/1ds-post-js'); + const appInsightsCore = new oneDs.AppInsightsCore(); + const collectorChannelPlugin: PostChannel = new postPlugin.PostChannel(); + // Configure the app insights core to send to collector++ and disable logging of debug info + const coreConfig: IExtendedConfiguration = { + instrumentationKey, + endpointUrl, + loggingLevelTelemetry: 0, + loggingLevelConsole: 0, + disableCookiesUsage: true, + disableDbgExt: true, + disableInstrumentationKeyValidation: true, + channels: [[ + collectorChannelPlugin + ]] + }; + + appInsightsCore.initialize(coreConfig, []); + + appInsightsCore.addTelemetryInitializer((envelope) => { + if (envelope.tags) { + // Sets it to be internal only based on Windows UTC flagging + envelope.tags['utc.flags'] = 0x0000811ECD; + } + }); + + return appInsightsCore; +} + +// TODO @lramos15 maybe make more in line with src/vs/platform/telemetry/browser/appInsightsAppender.ts with caching support +export class OneDataSystemWebAppender implements ITelemetryAppender { + + private _aiCoreOrKey: AppInsightsCore | string | undefined; + private _asyncAiCore: Promise | null; + + constructor( + private _eventPrefix: string, + private _defaultData: { [key: string]: any } | null, + iKeyOrClientFactory: string | (() => AppInsightsCore), // allow factory function for testing + ) { + if (!this._defaultData) { + this._defaultData = Object.create(null); + } + + if (typeof iKeyOrClientFactory === 'function') { + this._aiCoreOrKey = iKeyOrClientFactory(); + } else { + this._aiCoreOrKey = iKeyOrClientFactory; + } + this._asyncAiCore = null; + + // If we cannot fetch the endpoint it means it is down and we should not send any telemetry. + // This is most likely due to ad blockers + fetch(endpointUrl, { method: 'POST' }).catch(err => { + this._aiCoreOrKey = undefined; + }); + } + + private _withAIClient(callback: (aiCore: AppInsightsCore) => void): void { + if (!this._aiCoreOrKey) { + return; + } + + if (typeof this._aiCoreOrKey !== 'string') { + callback(this._aiCoreOrKey); + return; + } + + if (!this._asyncAiCore) { + this._asyncAiCore = getClient(this._aiCoreOrKey); + } + + this._asyncAiCore.then( + (aiClient) => { + callback(aiClient); + }, + (err) => { + onUnexpectedError(err); + console.error(err); + } + ); + } + + log(eventName: string, data?: any): void { + if (!this._aiCoreOrKey) { + return; + } + data = mixin(data, this._defaultData); + data = validateTelemetryData(data); + + try { + this._withAIClient((aiClient) => aiClient.track({ + name: this._eventPrefix + '/' + eventName, + data: { ...data.properties, ...data.measurements }, + + })); + } catch { } + } + + flush(): Promise { + if (this._aiCoreOrKey) { + return new Promise(resolve => { + this._withAIClient((aiClient) => { + aiClient.unload(true, () => { + this._aiCoreOrKey = undefined; + resolve(undefined); + }); + }); + }); + } + return Promise.resolve(undefined); + } +} diff --git a/src/vs/workbench/services/telemetry/browser/telemetryService.ts b/src/vs/workbench/services/telemetry/browser/telemetryService.ts index bd161f54f33..8e86627e42f 100644 --- a/src/vs/workbench/services/telemetry/browser/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/browser/telemetryService.ts @@ -10,6 +10,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILoggerService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; import { IStorageService } from 'vs/platform/storage/common/storage'; +import { OneDataSystemWebAppender } from 'vs/platform/telemetry/browser/1dsAppender'; import { WebAppInsightsAppender } from 'vs/platform/telemetry/browser/appInsightsAppender'; import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings'; import { ITelemetryData, ITelemetryInfo, ITelemetryService, TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; @@ -37,11 +38,20 @@ export class TelemetryService extends Disposable implements ITelemetryService { ) { super(); - if (supportsTelemetry(productService, environmentService) && productService.aiConfig?.asimovKey) { + if (supportsTelemetry(productService, environmentService) && productService.aiConfig?.asimovKey && productService.aiConfig?.ariaKey) { // If remote server is present send telemetry through that, else use the client side appender - const telemetryProvider: ITelemetryAppender = remoteAgentService.getConnection() !== null ? { log: remoteAgentService.logTelemetry.bind(remoteAgentService), flush: remoteAgentService.flushTelemetry.bind(remoteAgentService) } : new WebAppInsightsAppender('monacoworkbench', productService.aiConfig?.asimovKey); + const internalTesting = configurationService.getValue('telemetry.internalTesting'); + const appenders = []; + if (internalTesting) { + const telemetryProvider: ITelemetryAppender = remoteAgentService.getConnection() !== null ? { log: remoteAgentService.logTelemetry.bind(remoteAgentService), flush: remoteAgentService.flushTelemetry.bind(remoteAgentService) } : new OneDataSystemWebAppender('monacoworkbench', null, productService.aiConfig?.ariaKey); + appenders.push(telemetryProvider); + } else { + const telemetryProvider: ITelemetryAppender = remoteAgentService.getConnection() !== null ? { log: remoteAgentService.logTelemetry.bind(remoteAgentService), flush: remoteAgentService.flushTelemetry.bind(remoteAgentService) } : new WebAppInsightsAppender('monacoworkbench', productService.aiConfig?.asimovKey); + appenders.push(telemetryProvider); + } + appenders.push(new TelemetryLogAppender(loggerService, environmentService)); const config: ITelemetryServiceConfig = { - appenders: [telemetryProvider, new TelemetryLogAppender(loggerService, environmentService)], + appenders, commonProperties: resolveWorkbenchCommonProperties(storageService, productService.commit, productService.version, environmentService.remoteAuthority, productService.embedderIdentifier, productService.removeTelemetryMachineId, environmentService.options && environmentService.options.resolveCommonTelemetryProperties), sendErrorTelemetry: this.sendErrorTelemetry, }; From 187c93d88f956172cb5d04891a2b9d8521a441be Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Fri, 17 Jun 2022 13:21:32 -0700 Subject: [PATCH 162/175] Update codicons (#152512) - Add `map` and `map-filled` - Remap `debug-stackframe-dot` to `circle-small-filled` - Add `circle-small` https://github.com/microsoft/vscode-codicons/commit/2b98cb89ecb7b66cf6d41063aba4e246ee68e5be --- .../browser/ui/codicons/codicon/codicon.ttf | Bin 71264 -> 71580 bytes src/vs/base/common/codicons.ts | 6 +++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf index 94cbe346ed7e0c241d8727c6e802993c126847fc..e611595a0b2a99b0ffb001a852d82ec250c68164 100644 GIT binary patch delta 6572 zcmYk>d0bW1+6VC8b3{Q!K!L*`A_{6K4uA|A4j@jTi8G>^ObQ6jshM@mOpVkq%B-x& zYi4F<*{kODmYJE^WM*b&X67whi_FaIHe7putLOdW{nU3qXCHR^to`i0*R$5@{0_fO zFZtEQ`%(c}17KQdP4S$@)UM}&fNCIe%#7+greB!w-8(>nA8_VkMR{>q;iC;>c`T1X zdIi7mO!mCT&j%=>qGrM3Z^!?2gXe1mnk}oIU0U4dS7`>s9R!->)D$nC<9So=;ol>9 zeB7+!n(|MIqA&8~H!ktZU*^o7zu=LpA3qCmKj3|B{ek#a4cy`1fSfP-RIYDKmWKLR zd0G5-AD2Cz`mO%^U-2yT)Nl8Hx80$?k3^FLX!7@`B*qqi-y!%lsjIIGh!Fq!Z2=j+ zGty0N!WM~=Y6*~G_*q6^h&&?wWU%DoKBPz+EWv(!hkjCp+3-p|9>h}VB!d6oH@OS1 z;&E(~MKTdSsgMG>O>ULZGEHVkiA=_JnIL8Gzzzp#9$1@ zVjRY!5R>X>HSgr>gTA;KS?G@e$VLtZVKDMB0wZw?ZpCdVMj0wlg_)>E4Q62u=3*Y^ zV*zf*LM*~!+<`lB7nb2(tiUR)#{HDQhv)GEUcxTy#{Xas_F^CYh1c;04&qH5!dp0u&+!G$;wxOhH@Jv@<9itVfXnz1 zS8)x$;5vTAANUi038abmNec;*U~^8|5*1T%PjD z)A$nS_}G1o37ClK$iNH~$#j&X5~Uc81a!g`+=G>PSyu7+ioj%?7LW9nN(sbsGC=xE zGZ`hVq%8`h4g>KMmSYDl;fBPZi?o!cNJVet%1n7c?vs0E1vX2nq~X6P5#JN?q->J8 zGEe5q0=Zol${e&tp7>+EJdA1hC*Hvkyp7=)hM@@K3-=8Efl-($>DY)V2*-=qDb3Li zvG^9De4&5GXE=&?@gd&F2RM$8@d-Y~DZGbcQiInd8h4`^{1JfWIN1gt;RK@a3L0UN z$rr9!eTTq5>w5+I`vUp?A+*kI`F#UmrYXK(FvSXI7)z9NV=PsC(_qRJwlJ0}iDI0t zguRWKp(KE@Lh;>%sr0!GfB9#|D#aHTW~RarMt1^|M;L3A^kbZ*WH94w#aA3=j^fJ> zGgl#nah_ra05e~~w}c-SDE104w=4D!Fbfsjk1bN{Ghh}g_8u^ID0ms&xrjXq%$B4UQjo%IY@-ztL|Dcb`-2Jz?Cn5c^S>=M{TY78{mPZUc7%t^%}0rRP1 zxqvyPSU7kqKjRLuhQORwtR^s@E7ld5Gl~@k<_pDI19Mif>cD)dSbt#7DOMtwuM}$% z%z4G?1oO4y;}5$xt;$+eESa!}=)>1Ynyg4hdkJDGm-`{S}7^umOq#2H56` zLkHNFih~H)K!w+N|7|P9kp*m!!a>Gh#nA?Ah~l^dwzc921h$RhSOhjyaZ~~urZ_%< z4Obkgz2Yjfo-Qa^ns0490b9( zR~!z(#wiYrU^^%dk>Il(xkDT+!Nx0&n_v?ZM^Lbx6vtAqofSt_uw4}XV!TPYyJ-`Z zxZBuOiMx&6l(b+>`kN2pQr=yOOL-3^F6BLyxRm!&;?kU~#HBe!iA!^;5|`%QN?e-L zlpEyRbR{lp89ujRcka@bsl=tNj}n(QcFT^qwB4-4Wi3mI%UVArE^GajxJ(UD;xaW* ziOWv5;wY0Ja+J7~3{v7UlB>jJWUvyKks(T4MusYJ85yR;Wn{P#UYyNS;w~^>iMzlN z?*4Nl?g~dLaaVYY5_g3KN?hwlD{-wKqr^3StPl$nuvCdV zNtqINaJdrq_vuR9cQcgCVysXyo3T>KV#X>ZcQDRWvV^f($(@Y88g59IGP;wI+|B4t zM#2W#IZBo>&Q)&8YUe3g&NyGmO2!3BRx{qN_`xv8ZILYITuHht4F)nxapRMOq2ligY zNe}D_#pw_1eG2Cp-PIANM6jzACr7aEN{Q1XSa+qw2@|ZlQsUGJ_5sC76zm#>qAY%J z86eKAU|j}?b1Ybw0pe^6woY;01?#dw7+s&xHa__Z_a0H4i@~~_5NBnuE+>SS8C_0D zR@EeEC0OWv=Mf@!a&9s6sMA~I}|6CurDf3FJX5oPBdX( zQn+5ZQW;-Yoa@5Ap*ZV>J*ZH^_@>JfH}V8`4k>Xr>MbSi1|C*2m+@^S?!7#s z#J!jAC~@!QQ6=uZd{@ar#$$?eY}oe{XWR8(wrk={oEuiW7I(lZsP#*iRKF^RTBBr}eO(xg2oA89uCgP>FMW*w2+)X0vA$ z=l`%@D6Ry+o>g2Efc;WoD&slDbpqJ06juyj&nvDSz<#Z`ioj>(0(XcD3b5ZOxQFbb zlIHaR?c;sla?d?-giyxsm0L-)MsW!O)+#PWz&0u_OThl1xIh7WS#hZX_D97<3)m}) z%NMXeDK2EdUUhlmhHDzIKP#?oz+O{a=Yai1am54ny5iag?5_&$8TgOldI;=q3hqI7 z4JWRV!2Y4QS_1p0;<`zFVqA|X-qS`=Tt7jhN8v>MoVbWCuW+xa;;IT7n<+OaZ{(BZ zxRsXr)p2uu0|P4pPqhkYHKA2qtL;GuDhk>hbR_6%aAfep;9bEdLvlhILQLzR)|sty zTkmPpqRoOfCqvUit3&HTw}tKtJsNs4^it^cu;{RjVXuU}A08cE8@?y}sMq5i;a%$8 z;CNXqhH5`9WTfCjW3J8m{65)qtlYkp3WOOpX{=*%axmI69W_T5~n6^Nj#c( zt!wYDMP2uGz0@tQ+sbaQcJuw7RFqVkv@Pj+_vr4cyPxY3-6OBZ#-0T|m-oEXtDx7G zUf(8%B_}24CYL1FChtr>o_skaFr{lsPRg{D#VH$7cBULjMQT>+y3`L-ul63-ySn%4 zwAi%tw8FI7w9Dy%=~L5d)Ayy{$VkY@&6tw0I%8+Xxy=5Vg_%1uPxlGyGosHLU!Q}0 zuJ(=VThw=P-vfOw-duI_maN#Uihd*dt?0L>e@y>@{nz!sGGN$%bptL8EE>2o8`<9M zA=zuP4`-jv@ykibS&_4EP|l!+Tu*NA+!eXI273n&8C*U1@Zj@cgs z4J#XVY`Ay$tl^u6pU6wfTb*|yzeRqJ{4x3S@_iffuZ~C>Q9a_J5l2R19o?vOShNqFFjUzrmSz-lCu5fe&u=P z`^zs*Pno`N#<&?PW}K=BsK~8&yy9%-O_jqcH&-62LRC`L%Bn3hD`uXqUQ&IvCcS1J zu={ZQq{#Yr3%cjA?>fNc@CXhMf~1G1zodA>BGS`(caHBEB(a|Kt$r<926+6M2el0J z_lC3#ZSL3F8@{HgpC_PY3;&iu!7W;bcw2>gTl+QN9h3E9*zTVS0<(Qh_zHpD^ffL; z`g5rr8Q~de5ouk*BhoU$J7?!-=I6Yz4Kp*$%)gl#hMCd+Z`fuSN%%@yl0;u=iE{N% zl0-jTSG7t@k|en$*Hx+8J!eUhD=EL%>CNN$d4E6Kci->%^Eu~z&e{5!@5SA|8)7`M zfUEQr7?v*epkZ~w{9YXtmiD&`dxc#nKE3y4|+G)b;3SW@ltmfX+Zhw}YVRRxtL zpXG*M@&*1l#UK7!J#S&n53ANZ0o>+?2G?yo64P=Ugzu#zd)9Z`*cd0)WY`cdhRMZ$-1f0Z0CNE1DcYlfPc|V!RXZJp$h*YxyAo{NDsHYs@JEcsHpNoPDK!7^L!kde3|W05A0OOm9@aI8kWw8FjkJHE$YDMJ;4WdqjZ zK8ePk_!ZY>DfZ(jyd*V}jTKTN<0VgWB~xZfvD_^=cv&V(fj6spAy6L1|8N!` z;21u}M>v5`a0;h!2A|_NK9o5)Bq3N1KQu)%G{qHe;ERI~dCqhZ&6f9pMV& z9L1prGgl#vktOlCi#*PoN+n5*RZ3DB=P8a>m}u{a5Y}4I61)Fr#MZ(EK_hlTdp{Dz^qW5L|{CM z(+SKCFdBJRf@9>%ma$^4vecQ;U&fgmAG2fD$YtU4=K1W<#gn_fW zj*Mq~L`fdwIwiS`GauuHB$LtAj$|g|MkU3Ju6s!CX7nmfeK4C8US{Of?g(>;*{nD* z?rHV*kSyN0;uEJxn4OA~C5-z^;DoZ(?URh;W#PAks(FrO*T|1f71GXc!!ia7!1tYUWHGx|^75EBK=7m6tZ z=1axo0dr0yzmIV%mrf<^Bjy-%y=9?jl2+(AIy)6 zX%ObJVnT%ZNij9TTv1GtFh47%OPH&Qi4*1*#S{v2O);6m{HmB%VXi9=Gc3&i6mu=i z4aKYr^P6J+g}JGiiD7+Zkfelk!x4?!gu3%u>DXwK;BNSIPus8L-`z>si}F32$sqC9b=A{lx=u z{nA^B>z6)CT(|U9;<_bPiR+3uC9W&tmAJ0xr^Izde_FH5139(3 zrcG4hns$&9*R)AWT+3i689mcO57io zDVfVyuB3u-wvt7RbCfJ*bnhd%hta){WC^3Ek{6PD8LO1s$2d>PGRA5p%Nge@S;4qK z2`gb2DtUmhM#+PWt{^0}jIJOg4>7uekVn??g)0fkBaE&fZH7rB!+FDZAn zVRtHIGQO<1H-z1#xJ!h6MRC6fyIXPR2>Yty9ujts;%*Z5HHF_9T{94Om@%-g^M=rs zai5ZAjQf@Ncuz*ecv5+HP>Jh@LrT^#{#^+lAbVKJO2&UEcVuIaD7f}|Q%Nl2TZ$W6 z*tZq8wy^aI`Hb(lcH%``*S)J`3*&oAo?<+zq>AxAPb!+x%~Z-+hW1n`ST@r8Rt2_wBLI)r#!1D{ja&iIw$79aM!;$|P#jb1_?;|0Y7 z09ZGYiAMpj7Znc$V82#89)SHu@t^?qTZLB{FY%1dUAXZNkBpJ;yh9^HJZ{Jv#ghbB zt9YgW+o*WD0Q;ljIRmVFfQcs#us2_Sf$IY60(S=<4!jVQ7qmENL(qk`nQfQ0 zUDviDI3PGBI5&7z@aEus!N-DchD3y}1%L@X+wI@QU!~!*8`)(Qa?M;}Jd)`4KzX2eq$mzq9?#4uu_db+{548@W01P~`WK zH==@~>Y@%rosPQPF}dT?j(a+ukIsy4h-nj(9CM^oYNxZECv`s8rK-#Bu9;nrbt~+4 zse5$ygzn3_Z|{DhM~fcuJvQ{H?-|{5R?l@kPxtb~_bTkQve(I8S9|C8KG6GWpXff- zeY^F|?OPw)Ep}P#Td|FCzHt$8NpXd7wQ)P+8saX+`^1OGC&p*RSH;)HKNJ5&zo34l z{a);MynnC$Y5nv2UmxH`tV_6|8SGz7)UzY{kJvooSXyXWTH3C(b0aY_XJpODRilze z?HJWCI%V{l(Kp7-8nbJx&)C?prDN;UBhnYAKa>7$`uF4f#`PFiJFY&%gN!a2xfz=> zu8!|CK7V{;=G4qn6GA7fpU{wntlX^a6I)E|F>%bKgh}O-R!n++(&fqilT#+I&JM~> z$ga-bn|)>qrbJK4nR0n*^wi9$o2F$+yqbd_HJ1>-gFm;DolXBAa_Ic^ diff --git a/src/vs/base/common/codicons.ts b/src/vs/base/common/codicons.ts index 7021acc29ef..14be4c03c3a 100644 --- a/src/vs/base/common/codicons.ts +++ b/src/vs/base/common/codicons.ts @@ -428,7 +428,8 @@ export class Codicon implements CSSIcon { public static readonly debugBreakpointFunction = new Codicon('debug-breakpoint-function', { fontCharacter: '\\eb88' }); public static readonly debugBreakpointFunctionDisabled = new Codicon('debug-breakpoint-function-disabled', { fontCharacter: '\\eb88' }); public static readonly debugStackframeActive = new Codicon('debug-stackframe-active', { fontCharacter: '\\eb89' }); - public static readonly debugStackframeDot = new Codicon('debug-stackframe-dot', { fontCharacter: '\\eb8a' }); + public static readonly circleSmallFilled = new Codicon('circle-small-filled', { fontCharacter: '\\eb8a' }); + public static readonly debugStackframeDot = new Codicon('debug-stackframe-dot', Codicon.circleSmallFilled.definition); public static readonly debugStackframe = new Codicon('debug-stackframe', { fontCharacter: '\\eb8b' }); public static readonly debugStackframeFocused = new Codicon('debug-stackframe-focused', { fontCharacter: '\\eb8b' }); public static readonly debugBreakpointUnsupported = new Codicon('debug-breakpoint-unsupported', { fontCharacter: '\\eb8c' }); @@ -553,6 +554,9 @@ export class Codicon implements CSSIcon { public static readonly arrowCircleRight = new Codicon('arrow-circle-right', { fontCharacter: '\\ebfe' }); public static readonly arrowCircleUp = new Codicon('arrow-circle-up', { fontCharacter: '\\ebff' }); public static readonly heartFilled = new Codicon('heart-filled', { fontCharacter: '\\ec04' }); + public static readonly map = new Codicon('map', { fontCharacter: '\\ec05' }); + public static readonly mapFilled = new Codicon('map-filled', { fontCharacter: '\\ec06' }); + public static readonly circleSmall = new Codicon('circle-small', { fontCharacter: '\\ec07' }); // derived icons, that could become separate icons From 993da80fa29d5a5f77f0de5699433d803a043647 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Fri, 17 Jun 2022 13:52:49 -0700 Subject: [PATCH 163/175] Update codicons (#152516) --- .../browser/ui/codicons/codicon/codicon.ttf | Bin 71580 -> 71728 bytes src/vs/base/common/codicons.ts | 1 + 2 files changed, 1 insertion(+) diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf index e611595a0b2a99b0ffb001a852d82ec250c68164..20ffef74fa80b13310f5e764c8127b0bc6d77e6b 100644 GIT binary patch delta 7033 zcmXxp30&0G76GBmRwFYjvobT=&C0BIFKs@v^7ZWfuBWG;@0=MKX72y~|Nnc=J#Kj>X!q`*^=bYt zfUE^DZF+6_yq2Rs%>Y8?0rAsj*4$7v_|*KVK)M(Bd{Xs{^2%}dY#qh-o}q$j{zJgT zfMb0AGNo77E?oTmioY-O^BRHRRW);`m*?Cv>mZPF90)9}Enhq@;E=4~&*S<2lsV6lhK8REUz9So6PK$gP!uQgG{TQ~PF-IP*OOj_LWbbi# zF<|fNpg#li4_|o;0uLha%CjuSFd*nKf&w@4m!tVN5HM@ZTJxrP+lJaQkDDYz0`Mm;$uc~L z`|*SZ%I8dES06_|lpxCS+- z#T?AVwV00uScvPe2-jmVZom>O#WFuuU=?n`8r+84aR=7oPTYlc*nm20!riFHJ-8Q} zu>}v{LB8i9Y{kRaj>qvNc49Z4#XjuE0X&Zva1gJe0k7c@UdLg)fzR*-zT_40EzaUQ zoP&YIPxu+X;NSQS7x6p(fd4Q2S9n?_NSa8vL`k$Xljh=;Sn)}m#7lxCNwTC!sLR} zk$dGn*(~?VL$Vd8@fE(tHyDd?n2D~a#zdKkD$GVDMxqTe@GDm0R_u{Ay!_%Y37<-! z8P#2)1E3rb;31 z!(_x_7oL%@AxJ_pzDJDQiT~geyp5xHAMfHle1H=;iBtF($MBBK!Hd!YegwmV5QO3* z9LI-9#9sV}Msx%3TM4eqiMU+XAWiBzMFxj%;SI(=ye*3&EKyej-S*W-kz+9)eGQccSDB{GK>-mDXT)-?=TsUBE zQ1CG>QCv)5+y@hv6_}+8H!=Q0GWdL%;-UlNS6qHzmMdIh{`@P}NJU{)%wPB1qs zu2V3p6rNzbMTr}vn*rh~26L-|`|veN-1OhBWCY_KN~STcRWgNf@;W|{jAV32L{iSU zLAhn!a7lK=T^n_ZOC^k(4Z>55cPkmo=uRhbA>9?VjteQwql!x^%r?cv6~W|zVcZCaOEk<*#l;%tX?KP3f$KKRF2xlb<{8Dc z9A>xTst)t4;`$D=M{%Wx*{itb!|YRB{bAhsBIW^@{fZd@=73^u;1%{fUl7v-%nOPM z1Lj4=)B*F7ViJLQSuvf!yrP&`U=Av#7?@WTlMPIRV%mXuO)&w%9CCPtF&DwSu29T) zSTR4ryrGz>V2&u}ESNVHvlq#F!MvxKy(6#9R;aFU5=wV-)j0j8&`zU>X%`0+=5as{@#y6zc?-pB2{e{F`4C ziwBrr70U>i^NIxp%)b>&3z!RvMFz}oisc5(Ma9Ac=6A&s1m+LLVg%+-#j*tE68ZVS z3I*mr>dKM*#plGT1?ImBTNy7aRx&VuE7mkDPZX;gSW&EVU;`8@9@s#|+6OjBu?m9U zHsK3mK?EDDSQ^246pJL-5XEu{HdL{2f(=(JpFWTPYTPut|y~AZ)T?F$kNYSQf&jDi(;atrbf}*fhnW5jI`1e1vVI zTpqZ>HMMn*SqKOdxyPu$2!?qPHzC%Kn#nUeb$-C#*J zGrFT7xu0=`l7|@GL6B@^bO%A)@xiWA-21`aqPPp>{lA(oh#Nv!ccjFvA?zB(O(Lv2 zYT|Yg)*Ur*;|S}Hnz)68U8^vW@lM5UC9Inb;sz7e%?5F+3F~HKJ$RM43t@xe{u9t=?yCxzXlxI2ZtTXCNXTd%ldg>|z>*u&^%k7P~V?Jd&$?h@ak@G0X1N&*=l zRFco==8(heM)hc4ZBluzYY7e;?5g(m*O5A_8Eo08Fwr0%XyzY%NK-pjBb8N zA{h57Zrfq^DQ@6lpHtk*!|qq;%XmPEyXT%)uHR-~P_mlwMJ4X4d&%+h%iLx6vVxno zSCqIfJE+(tfPGc5UjW;n+;w7KQ{qnJAtmnAy{^Qa(!)yTGrpl@0i*v2A4nE5zNzFo z#excYwf&Efp zD&uLzt_ti|iv1PXGm4!S*sm3PF0kJyc3)t>Rd83@Ss>FrNElyyr`Vf;J*U{Ef&E_L z`?{j!IRBk|WfXfiuvW2)1KX(B&w>3>v9kmFlVXnt_GiUz59}|BeIM9g6+1w%=M{TF zu>V%<3c+6BCfGf&K?M7oVyg)DqGHns_ICw$+W$~&B*Ffv;4bbT5BF{DL*|fgt>8OCH?5NREd!ibnqobEaf6*+X z*`Q_%n=Nm4zIngqyJMnb=Ev-aITmv^=7QfFGdvm>I-jm*+VnbrfVvoe0_XYWi zd~1Bq`%d^S#rfj0;-H`H`3dz2dlF721}Ekwj!s;f zxFhj+i;x!6T71*e+cLXlRm;Pzf?F+abuuX;X-d+Oa&hyo$Z+0v8?0a?8NM{?A6&PJFV?>uycCn zwVlu8w9XlxQ=PLs=fRvKIVLwEH#2uYZdLBG+%38La!>ekOfGuA91=Zr*OgyRGT= zR=2;pXLp~}y}tXt?tk=1>9M`XnVu7RZtJzCS3|G!MdON=79H)K)q7R%qs2+Z^~I)7 z&pspjtnBkv2}{N*YQoT(#_~FG`C_=an{;HufFacV*ul{m`#vzs!DT`w!|r zr~eNFIuF=7FnHj|flm(nWKh#V)q@TXP8!^A@a(~N4c;~Q#E|SE)kAg+d2eXI(4Ip# z4?Q+4WZ0%*m#e;+%^Tc0 zqivjy)x-a3ct5tqm4PLQ8>lzBSB)nm##0&|%5la@#+5?HX$@r zM;aKh)0>d!i}`1qu~E(47(=i8!d`#Y%9HZ&fdG->z<_{94wMA(KQh9@DrVHwbX-tV NzM%R*RTIf?{y+L|nq>e0 delta 6896 zcmXBZ3w(}c9|!Q?e|z>kvm3#M7F8@z`@Bg0d>AtV~y082C|F5fU+dS)E^{h_uX9Kbl zz~u6orE}`C)6W6H)j<4+Y1Ma3JwN7$4}nw3CO72QyP9_WQa^hO^H!e9)+&6tQ=P>Kpvq6#xmjhUE* zIk**bF%R=`8y4VpEW{nS6L(<={P$uR?#Bu|fEqlAm3RoNuo{n`7LVdFti|Ja0#9K* zp5}L+!N0Hp&*6Ezh?npxwqZNoz)tMKZoG|m@Gkb@J?zK(IDoJ4HO}B$oX2;#fba1m z41U68{ET{B#c#NV-|;v8>&HI=2^5cnNQ6X6Lun*Y5-l+jD{&Gp%_K>hOR}_(mXac= zk|wRBjigJ4w3GJIK{`sNWJ$hslFrgax=J_cDZQjndP`sFCj(@l43Z&ovy70DGD=2E zv5b=vnIMy-RLW(F_-Dy%StxhNBDqr*%iZFaC324}mF2QR9*`P&NLIVAsm3cB>Zj%Kv2aPaLd{`roVlv*shd782FaZ7057B%R|BYuc6q6(m>o5T^ zcm-Rf0h*zA5-uW&ulis35{K{+j^YSD!7+S>&+!FL;$s|^nRrtYaW}m1As7vC0-xeI z67V|eV9{xN`>=5FujU)+Udj!CnXLHA!IUbTW-L?Ema$y%<%6kE*u*$RNdn_kC7gN8 zG$p}|m5Q$`%yi|)ul%7(@g0Vlq0pDnoq*&q#+gdGGtN@dhjF&zTMsiw@%@LnRUwOU zuHu{kGf#1LfSJz`zyml@z}%)dWxy;@a2LB>aT;<@)rH>Ag+1K73VUT`xV@U zRw!{DU!!Cg>9>u||n|n2#ta4k%)X&%Ns zB;te*^M>No53^G-3Bc@9Ob33L-TXkz3@~phb|s7(gT!nB^Oj=XfMGs%m_cCPR?H~Q^I_ym@{EcDP~WYuN3nr%xUFkN`?7aF}cE=QB1Qi-zX+rn6rwh7v@{VBn)#- zF&)GFM=>$OoL5ZIFyARAYnTg)X&dHy_x!)--7n#XiwgHL{-BuDVJ<1AcbFd)lQ@h~ zO!+WYG5N#PDV6~+KPeUjFqaie1DKx`R`L0pD~k03%rA*?;*nu~32epJK@Zb6sHr z<3EaJ40D%aK?5s_r44L=Vvz$Is95g6dK3#E_-znB5NjY>rbiU+D3{c7i?q2;tMuPu?&NaRxHS1 zV-!m>*jU9P4K_}(T!W2QEZkrd6iYbRM8#qbwh6CqoAB;AuxwMs3JI){2i5wv7_^ z9MhG!=h#+B2xEp4*X8Y$xGrz6#C3TGC9cOiy64Y>xNgo=;<`CYiRb+$l^;l4w{=tEx~;ns*KGw#Tu=2-;(Ds5 z64yJul(^n0RN{K1w-VPQMM_+c^l|*WuL8KA`7;Xozs4hJc5 zcQ{yyyTc($+zsBW#NFU9C9d)Q;XIJI){ju)T0c^WYyBuCu92gaxJHgq;uSAKC9ouI^BbfOY>(Md|&MQ>5!PCi+QJ9()Rck(hN?tJA++&LJ^!h^d-uy}N@g)uDw)kVUCBbmDkXO?&QP+5v0BNUj5C!iW_0Hwxtr0Q zi-eEd&QY?&eYjP*#k-xWWGUl3CCeG-D_OyKo010@7bvM=yj{sdjIOaHs~BBlNgif& zjU`#b=o;(iK_1}`uC*j<8Shf^IHPMh$vQ^YSdu3gmneCX(KVdp8AjJ|;?fTGUd2To z>@vmW9_)Pz=iK}6?vA(yguP#JH3;kOl(;U0b$3c!A;P*lC9V`;A5>g5!md;(VRSt} zTuH*Z9w4qMVOZ{R3!1Hp9%amfkmdV{$5gsoLvhQdCoxFCgnOmS%n>w1j1 zNQHGhMtF_U^%%+hwX2(?_}zG4uW*X-X(a)SuHQ&<7+t@SxF&8;T{i8PH|(nl*BQ4dF2(ttxjrGZVtidmDC2g;l{)MW#Wg$Z8;YxU*qsVRjJuS$ z_iwinHxS-bav$R!C2j=1<@kBWZV2vGa9#Me5_hq86gLK7-&NcqfZeB1#`vC+rx^Du zagXtRCGPPaP;x8d2TJBL`VaCzGLP{?CG!~%DRD3LM@kkj9#-6ufc;o;Zvys+!a&AP z6!$A&k1DKT{8Vud1NNBmV={mEOmSla_PF8}2khsHn;ozx6t_KKzfjx&fjz0X6$1OE z;-(0!8_dM*5!kPk+p@H$6}L=azvh~S2XOZU_Ke~_3hXxulNiq`?ybOntGLSodro0O z?UkfBzZ-VnDY#*GL2(xb_It(s7}$#nNsK=zZqdNHAxem1{870bT5A+HaA2+CRt{{P z;-(JlPwqY9f%`kKmlbzO6`YQm6}3+?qn2SGhvJj$_S1C!qFNa;Cq)vy%Vp2?`5p9dyXM*t^%)*H_^? z5?l~`t-+QCS3=@L(nIEkEDKp1vN>c=$cd1PA@!jRL%W4mg&qyP7M2m#GpsV~WO#7+ znDEu%TOtrq60tGjU_^aneB^@2ZILG$7B<|_&@_r@)TvQXqn(XI8qaThA}TMcI%;*) z=BV9Khx}0|qAo>Ui%yJQ7yWwlk(k7onwXt2hhhU_2gfdsT^svO?CIDmaV_JD;wt0T z$8C*oA3rI6S^V+%D+#d)c?l&63li2P98S2F7@ycPu{!b5#3M~on>^p-RFnFq=}lKO zJ>0BYvvtjmCACbdNjlyEogP6bxoVFHUryCYO|@$p*C03bJ9!Fcc))!JFxBY zwmaJXl~IyWld(DDTD!z{E83lHpV)q2`*j_Lby(WrQpaH(H+8(28J(GtS(I6pS(CXn z^H}EPtgx)~tir6xSqrn)W^MIn9n3~{x9nBfN3-j5M&(rJtjJBu&C4y$t;xNd7nV0E zuO@GI-u3*{{G$8``783b=AZ3U(5bl7)=sB7M|U3Fd1dE)o$I?KcPZ(zu*=>q7rIt; z-PA3qTV?mb-IsOWScv&yt>7d!bisufDxj_Bzn(OrfVRwa~w; zaCh&*-W!SnigJpU6>aMi+ox}z>OKehoa=MB@8*6H{j&PK-0y1tivEWO#15D>VEusO z12YD$7>99c4Q(a4uaUKzDy)JFfPGozc1?m4=8^s3PZ#*~a% zHD=FPjNLufPFmhwI2hs#e_bg5WWv1f{B%D^dmrd*hsHFfv2QPY-9 zJ6RcASycIC<(cWNr}v+puyOkSDpX}uEw9=%qjJWn>P6M{GxKK7<$5+c^);VAk*Tb} z&6D}D&Bd1z?M-Q!)+#MGrB!}jeqMLU;W0li+v{uT^ZFvh=WQm@K5wLCw@T|ES-vd( zF*hYTE=lsEy)Dyn^0H#D271Cmdc{QsM|eUD8-#i!ATlyMCZeIo7aSB39uykj4G0Jf z-r@5EN_cVLAYY(2%^T=j-lBaA{(E0kcyf4TtT!k$GCVmlI4mSRwt2&FZ%|OACos$t z;f;(;4~+ Date: Fri, 17 Jun 2022 14:25:54 -0700 Subject: [PATCH 164/175] Don't hardcode use of github slugifier (#152507) Use `engine.slugifier` for this instead --- .../src/tableOfContents.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/extensions/markdown-language-features/src/tableOfContents.ts b/extensions/markdown-language-features/src/tableOfContents.ts index 6935763b3ec..0f28f9fa635 100644 --- a/extensions/markdown-language-features/src/tableOfContents.ts +++ b/extensions/markdown-language-features/src/tableOfContents.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; import { MdDocumentInfoCache } from './languageFeatures/workspaceCache'; import { MarkdownEngine } from './markdownEngine'; -import { githubSlugifier, Slug } from './slugify'; +import { githubSlugifier, Slug, Slugifier } from './slugify'; import { Disposable } from './util/dispose'; import { isMarkdownFile } from './util/file'; import { MdWorkspaceContents, SkinnyTextDocument } from './workspaceContents'; @@ -65,7 +65,7 @@ export class TableOfContents { public static async create(engine: MarkdownEngine, document: SkinnyTextDocument,): Promise { const entries = await this.buildToc(engine, document); - return new TableOfContents(entries); + return new TableOfContents(entries, engine.slugifier); } public static async createForDocumentOrNotebook(engine: MarkdownEngine, document: SkinnyTextDocument): Promise { @@ -82,7 +82,7 @@ export class TableOfContents { } } - return new TableOfContents(entries); + return new TableOfContents(entries, engine.slugifier); } } @@ -103,11 +103,11 @@ export class TableOfContents { const lineNumber = heading.map[0]; const line = document.lineAt(lineNumber); - let slug = githubSlugifier.fromHeading(line.text); + let slug = engine.slugifier.fromHeading(line.text); const existingSlugEntry = existingSlugEntries.get(slug.value); if (existingSlugEntry) { ++existingSlugEntry.count; - slug = githubSlugifier.fromHeading(slug.value + '-' + existingSlugEntry.count); + slug = engine.slugifier.fromHeading(slug.value + '-' + existingSlugEntry.count); } else { existingSlugEntries.set(slug.value, { count: 0 }); } @@ -163,14 +163,15 @@ export class TableOfContents { return header.replace(/^\s*#+\s*(.*?)(\s+#+)?$/, (_, word) => word.trim()); } - public static readonly empty = new TableOfContents([]); + public static readonly empty = new TableOfContents([], githubSlugifier); private constructor( public readonly entries: readonly TocEntry[], + private readonly slugifier: Slugifier, ) { } public lookup(fragment: string): TocEntry | undefined { - const slug = githubSlugifier.fromHeading(fragment); + const slug = this.slugifier.fromHeading(fragment); return this.entries.find(entry => entry.slug.equals(slug)); } } From 5ffcfde11d8b1b57634627f5094907789db09776 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Fri, 17 Jun 2022 20:35:46 -0400 Subject: [PATCH 165/175] Fix 1DS part A internal flag (#152515) --- src/vs/platform/telemetry/browser/1dsAppender.ts | 8 ++++---- src/vs/platform/telemetry/node/1dsAppender.ts | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/platform/telemetry/browser/1dsAppender.ts b/src/vs/platform/telemetry/browser/1dsAppender.ts index eb91e96acd3..74dfaa90175 100644 --- a/src/vs/platform/telemetry/browser/1dsAppender.ts +++ b/src/vs/platform/telemetry/browser/1dsAppender.ts @@ -33,10 +33,10 @@ async function getClient(instrumentationKey: string): Promise { appInsightsCore.initialize(coreConfig, []); appInsightsCore.addTelemetryInitializer((envelope) => { - if (envelope.tags) { - // Sets it to be internal only based on Windows UTC flagging - envelope.tags['utc.flags'] = 0x0000811ECD; - } + envelope['ext'] = envelope['ext'] ?? {}; + envelope['ext']['utc'] = envelope['ext']['utc'] ?? {}; + // Sets it to be internal only based on Windows UTC flagging + envelope['ext']['utc']['flags'] = 0x0000811ECD; }); return appInsightsCore; diff --git a/src/vs/platform/telemetry/node/1dsAppender.ts b/src/vs/platform/telemetry/node/1dsAppender.ts index be4ae256cb1..8f604ef9cc1 100644 --- a/src/vs/platform/telemetry/node/1dsAppender.ts +++ b/src/vs/platform/telemetry/node/1dsAppender.ts @@ -67,10 +67,10 @@ async function getClient(instrumentationKey: string): Promise { appInsightsCore.initialize(coreConfig, []); appInsightsCore.addTelemetryInitializer((envelope) => { - if (envelope.tags) { - // Sets it to be internal only based on Windows UTC flagging - envelope.tags['utc.flags'] = 0x0000811ECD; - } + envelope['ext'] = envelope['ext'] ?? {}; + envelope['ext']['utc'] = envelope['ext']['utc'] ?? {}; + // Sets it to be internal only based on Windows UTC flagging + envelope['ext']['utc']['flags'] = 0x0000811ECD; }); return appInsightsCore; From 5a175207de7c6d0821b3318cfdbe73bb9448ed57 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Sat, 18 Jun 2022 21:25:54 -0700 Subject: [PATCH 166/175] Improve markdown link regexp (#152533) * Improve markdown link regexp This makes the markdown link regexp more readable and also combines the two regular expressions we were running * Fixed backtracking --- .../languageFeatures/documentLinkProvider.ts | 74 ++++++++++--------- .../src/test/documentLinkProvider.test.ts | 10 +-- 2 files changed, 45 insertions(+), 39 deletions(-) diff --git a/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts b/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts index ab7d3b14bcc..b0505dc283b 100644 --- a/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts @@ -131,11 +131,14 @@ export type MdLink = MdInlineLink | MdLinkDefinition; function extractDocumentLink( document: SkinnyTextDocument, - pre: number, - link: string, + pre: string, + rawLink: string, matchIndex: number | undefined ): MdLink | undefined { - const offset = (matchIndex || 0) + pre; + const isAngleBracketLink = rawLink.startsWith('<'); + const link = stripAngleBrackets(rawLink); + + const offset = (matchIndex || 0) + pre.length + (isAngleBracketLink ? 1 : 0); const linkStart = document.positionAt(offset); const linkEnd = document.positionAt(offset + link.length); try { @@ -185,20 +188,36 @@ function stripAngleBrackets(link: string) { return link.replace(angleBracketLinkRe, '$1'); } -/** - * Matches `[text](link)` - */ -const linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]]|\][^(])*\])\(\s*)(([^\s\(\)]|\([^\s\(\)]*?\))+)\s*("[^"]*"|'[^']*'|\([^\(\)]*\))?\s*\)/g; +const r = String.raw; /** - * Matches `[text]()` + * Matches `[text](link)` or `[text]()` */ -const linkPatternAngle = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]]|\][^(])*\])\(\s*<)(([^<>]|\([^\s\(\)]*?\))+)>\s*("[^"]*"|'[^']*'|\([^\(\)]*\))?\s*\)/g; +const linkPattern = new RegExp( + // text + r`(\[` + // open prefix match --> + /**/r`(?:` + + /*****/r`[^\[\]\\]|` + // Non-bracket chars, or... + /*****/r`\\.|` + // Escaped char, or... + /*****/r`\[[^\[\]]*\]` + // Matched bracket pair + /**/r`)*` + + r`\]` + + // Destination + r`\(\s*)` + // <-- close prefix match + /**/r`(` + + /*****/r`[^\s\(\)\<](?:[^\s\(\)]|\([^\s\(\)]*?\))*|` + // Link without whitespace, or... + /*****/r`<[^<>]*>` + // In angle brackets + /**/r`)` + + + // Title + /**/r`\s*(?:"[^"]*"|'[^']*'|\([^\(\)]*\))?\s*` + + r`\)`, + 'g'); /** - * Matches `[text][ref]` or `[shorthand]` - */ +* Matches `[text][ref]` or `[shorthand]` +*/ const referenceLinkPattern = /(^|[^\]\\])(?:(?:(\[((?:\\\]|[^\]])+)\]\[\s*?)([^\s\]]*?)\]|\[\s*?([^\s\]]*?)\])(?![\:\(]))/gm; /** @@ -270,36 +289,23 @@ export class MdLinkComputer { private *getInlineLinks(document: SkinnyTextDocument, noLinkRanges: NoLinkRanges): Iterable { const text = document.getText(); - - for (const match of text.matchAll(linkPatternAngle)) { - const matchImageData = match[4] && extractDocumentLink(document, match[3].length + 1, match[4], match.index); - if (matchImageData && !noLinkRanges.contains(matchImageData.source.hrefRange)) { - yield matchImageData; - } - const matchLinkData = extractDocumentLink(document, match[1].length, match[5], match.index); - if (matchLinkData && !noLinkRanges.contains(matchLinkData.source.hrefRange)) { - yield matchLinkData; - } - } - for (const match of text.matchAll(linkPattern)) { - const matchImageData = match[4] && extractDocumentLink(document, match[3].length + 1, match[4], match.index); - if (matchImageData && !noLinkRanges.contains(matchImageData.source.hrefRange)) { - yield matchImageData; - } - - if (match[5] !== undefined && match[5].startsWith('<')) { - continue; - } - - const matchLinkData = extractDocumentLink(document, match[1].length, match[5], match.index); + const matchLinkData = extractDocumentLink(document, match[1], match[2], match.index); if (matchLinkData && !noLinkRanges.contains(matchLinkData.source.hrefRange)) { yield matchLinkData; + + // Also check link destination for links + for (const innerMatch of match[1].matchAll(linkPattern)) { + const innerData = extractDocumentLink(document, innerMatch[1], innerMatch[2], (match.index ?? 0) + (innerMatch.index ?? 0)); + if (innerData) { + yield innerData; + } + } } } } - private *getAutoLinks(document: SkinnyTextDocument, noLinkRanges: NoLinkRanges): Iterable { + private * getAutoLinks(document: SkinnyTextDocument, noLinkRanges: NoLinkRanges): Iterable { const text = document.getText(); for (const match of text.matchAll(autoLinkPattern)) { diff --git a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts index 78c1ceee590..2b9ac060be1 100644 --- a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts +++ b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts @@ -32,7 +32,7 @@ function assertLinksEqual(actualLinks: readonly vscode.DocumentLink[], expectedR } } -suite('markdown.DocumentLinkProvider', () => { +suite('Markdown: DocumentLinkProvider', () => { test('Should not return anything for empty document', async () => { const links = await getLinksForFile(''); assert.strictEqual(links.length, 0); @@ -131,24 +131,24 @@ suite('markdown.DocumentLinkProvider', () => { { const links = await getLinksForFile('[![alt text](image.jpg)](https://example.com)'); assertLinksEqual(links, [ + new vscode.Range(0, 25, 0, 44), new vscode.Range(0, 13, 0, 22), - new vscode.Range(0, 25, 0, 44) ]); } { const links = await getLinksForFile('[![a]( whitespace.jpg )]( https://whitespace.com )'); assertLinksEqual(links, [ + new vscode.Range(0, 26, 0, 48), new vscode.Range(0, 7, 0, 21), - new vscode.Range(0, 26, 0, 48) ]); } { const links = await getLinksForFile('[![a](img1.jpg)](file1.txt) text [![a](img2.jpg)](file2.txt)'); assertLinksEqual(links, [ - new vscode.Range(0, 6, 0, 14), new vscode.Range(0, 17, 0, 26), - new vscode.Range(0, 39, 0, 47), + new vscode.Range(0, 6, 0, 14), new vscode.Range(0, 50, 0, 59), + new vscode.Range(0, 39, 0, 47), ]); } }); From 4c72dedb4ad283a569f83a7389468c3ae2c642c3 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Sun, 19 Jun 2022 09:40:10 -0700 Subject: [PATCH 167/175] Optimize markdown workspace scanning (#152563) * Optimize markdown workspace scanning - Adds cache for markdown file - Avoid reading non-markdown files from disk (when we expect markdown files) - Use `range.contains(pos)` instead of `range.intersects(range)` * Don't remove cached document on change We only want to invalidate the cached document when it is first opened (since the cached version is the one from disk). Otherwise we can use the live version of the doc --- .../languageFeatures/documentLinkProvider.ts | 2 +- .../src/workspaceContents.ts | 29 ++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts b/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts index b0505dc283b..5d33055264e 100644 --- a/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts @@ -260,7 +260,7 @@ class NoLinkRanges { contains(range: vscode.Range): boolean { return this.multiline.some(interval => range.start.line >= interval[0] && range.start.line < interval[1]) || - this.inline.some(position => position.intersection(range)); + this.inline.some(inlineRange => inlineRange.contains(range.start)); } } diff --git a/extensions/markdown-language-features/src/workspaceContents.ts b/extensions/markdown-language-features/src/workspaceContents.ts index 02477c8c9d7..6608f2b756c 100644 --- a/extensions/markdown-language-features/src/workspaceContents.ts +++ b/extensions/markdown-language-features/src/workspaceContents.ts @@ -4,11 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import * as URI from 'vscode-uri'; import { coalesce } from './util/arrays'; import { Disposable } from './util/dispose'; import { isMarkdownFile } from './util/file'; import { InMemoryDocument } from './util/inMemoryDocument'; import { Limiter } from './util/limiter'; +import { ResourceMap } from './util/resourceMap'; /** * Minimal version of {@link vscode.TextLine}. Used for mocking out in testing. @@ -62,6 +64,8 @@ export class VsCodeMdWorkspaceContents extends Disposable implements MdWorkspace private _watcher: vscode.FileSystemWatcher | undefined; + private readonly _documentCache = new ResourceMap(); + private readonly utf8Decoder = new TextDecoder('utf-8'); /** @@ -118,6 +122,7 @@ export class VsCodeMdWorkspaceContents extends Disposable implements MdWorkspace this._watcher = this._register(vscode.workspace.createFileSystemWatcher('**/*.md')); this._register(this._watcher.onDidChange(async resource => { + this._documentCache.delete(resource); const document = await this.getMarkdownDocument(resource); if (document) { this._onDidChangeMarkdownDocumentEmitter.fire(document); @@ -132,14 +137,23 @@ export class VsCodeMdWorkspaceContents extends Disposable implements MdWorkspace })); this._register(this._watcher.onDidDelete(resource => { + this._documentCache.delete(resource); this._onDidDeleteMarkdownDocumentEmitter.fire(resource); })); + this._register(vscode.workspace.onDidOpenTextDocument(e => { + this._documentCache.delete(e.uri); + })); + this._register(vscode.workspace.onDidChangeTextDocument(e => { if (this.isRelevantMarkdownDocument(e.document)) { this._onDidChangeMarkdownDocumentEmitter.fire(e.document); } })); + + this._register(vscode.workspace.onDidCloseTextDocument(e => { + this._documentCache.delete(e.uri); + })); } private isRelevantMarkdownDocument(doc: vscode.TextDocument) { @@ -147,17 +161,30 @@ export class VsCodeMdWorkspaceContents extends Disposable implements MdWorkspace } public async getMarkdownDocument(resource: vscode.Uri): Promise { + const existing = this._documentCache.get(resource); + if (existing) { + return existing; + } + const matchingDocument = vscode.workspace.textDocuments.find((doc) => this.isRelevantMarkdownDocument(doc) && doc.uri.toString() === resource.toString()); if (matchingDocument) { + this._documentCache.set(resource, matchingDocument); return matchingDocument; } + const ext = URI.Utils.extname(resource).toLowerCase(); + if (ext !== '.md') { + return undefined; + } + try { const bytes = await vscode.workspace.fs.readFile(resource); // We assume that markdown is in UTF-8 const text = this.utf8Decoder.decode(bytes); - return new InMemoryDocument(resource, text, 0); + const doc = new InMemoryDocument(resource, text, 0); + this._documentCache.set(resource, doc); + return doc; } catch { return undefined; } From 5dd9d5d49196285536422e18205fe5dea59833e1 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Mon, 20 Jun 2022 10:32:27 +0200 Subject: [PATCH 168/175] Fix port argument (#152541) (#152617) --- test/automation/src/playwrightBrowser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/automation/src/playwrightBrowser.ts b/test/automation/src/playwrightBrowser.ts index f4e28668ab0..cfbae17dd00 100644 --- a/test/automation/src/playwrightBrowser.ts +++ b/test/automation/src/playwrightBrowser.ts @@ -44,7 +44,7 @@ async function launchServer(options: LaunchOptions) { const args = [ '--disable-telemetry', '--disable-workspace-trust', - `--port${port++}`, + `--port=${port++}`, '--enable-smoke-test-driver', `--extensions-dir=${extensionsPath}`, `--server-data-dir=${agentFolder}`, From 63ea7eae19563c97935527c22771d6559816bd5a Mon Sep 17 00:00:00 2001 From: Robo Date: Mon, 20 Jun 2022 19:31:20 +0900 Subject: [PATCH 169/175] chore: bump electron@18.3.4 (#152624) --- .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 be7ab934758..e6d816a2f56 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,4 +1,4 @@ disturl "https://electronjs.org/headers" -target "18.3.3" +target "18.3.4" runtime "electron" build_from_source "true" diff --git a/cgmanifest.json b/cgmanifest.json index 5d239f2547d..972ef7c833e 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -60,12 +60,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "15f3c45fe9292fb80c5b5c4d26a218e825287d07" + "commitHash": "7162f641b5761e737ca5a9b3ef6d3e7454ee3dbd" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "18.3.3" + "version": "18.3.4" }, { "component": { diff --git a/package.json b/package.json index f06aff9e674..68b84062f86 100644 --- a/package.json +++ b/package.json @@ -138,7 +138,7 @@ "cssnano": "^4.1.11", "debounce": "^1.0.0", "deemon": "^1.4.0", - "electron": "18.3.3", + "electron": "18.3.4", "eslint": "8.7.0", "eslint-plugin-header": "3.1.1", "eslint-plugin-jsdoc": "^39.3.2", diff --git a/yarn.lock b/yarn.lock index fadbc67e755..b8d06e9ce7c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4335,10 +4335,10 @@ electron-to-chromium@^1.4.17: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.45.tgz#cf1144091d6683cbd45a231954a745f02fb24598" integrity sha512-czF9eYVuOmlY/vxyMQz2rGlNSjZpxNQYBe1gmQv7al171qOIhgyO9k7D5AKlgeTCSPKk+LHhj5ZyIdmEub9oNg== -electron@18.3.3: - version "18.3.3" - resolved "https://registry.yarnpkg.com/electron/-/electron-18.3.3.tgz#1c48273c1ad1522b8c18f19575e862c7ccd9f409" - integrity sha512-LYxf3uCDc/r0klu7LL0eZLxkseoGIY/vrCfS0Qj4YTU3M7LLjOaIqrajI7icKwaI2dgxiuJJH3n4eqALFpJAFg== +electron@18.3.4: + version "18.3.4" + resolved "https://registry.yarnpkg.com/electron/-/electron-18.3.4.tgz#efbc0d1c794bccac62e95556d4e355e8a3eca9a4" + integrity sha512-MIxQnaQR7NzWZOuAhRbJAf+pPyoXXGNge9E6pz7nGLXE/DxeN6BUbKb0iWVCCMkxIpqdIhXdc5sViUCX74dvsw== dependencies: "@electron/get" "^1.13.0" "@types/node" "^16.11.26" From c8663870df8d51bb5a7629d695f2020330190ebe Mon Sep 17 00:00:00 2001 From: John Murray Date: Mon, 20 Jun 2022 07:40:59 -0700 Subject: [PATCH 170/175] Preserve spaces in testing labels using icons (#152630) (#152646) --- src/vs/workbench/contrib/testing/browser/media/testing.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/testing/browser/media/testing.css b/src/vs/workbench/contrib/testing/browser/media/testing.css index a58463622f5..7f42e463ff6 100644 --- a/src/vs/workbench/contrib/testing/browser/media/testing.css +++ b/src/vs/workbench/contrib/testing/browser/media/testing.css @@ -21,6 +21,10 @@ align-items: center; } +.test-explorer .test-item .label { + white-space: pre-wrap; +} + .test-explorer .test-item, .test-output-peek-tree .test-peek-item { display: flex; From b3dc3301dd22d9695d5e5bdc69e5e23a6db1be58 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 20 Jun 2022 19:12:23 +0200 Subject: [PATCH 171/175] ability to switch profile without reload (#152652) * ability to switch profile without reload * fix tests --- .../common/extensionManagementIpc.ts | 25 +++---- .../userDataProfile/common/userDataProfile.ts | 12 +++- .../electron-main/userDataProfile.ts | 2 +- .../electron-sandbox/userDataProfile.ts | 2 +- .../parts/activitybar/activitybarPart.ts | 11 ++- .../extensionsActions.test.ts | 30 ++++---- .../electron-browser/extensionsViews.test.ts | 5 +- .../extensionsWorkbenchService.test.ts | 12 ++-- .../browser/userDataProfile.ts | 22 ++++-- .../configuration/browser/configuration.ts | 46 +++++++----- .../browser/configurationService.ts | 2 +- .../configuration/common/configuration.ts | 7 ++ .../browser/extensionEnablementService.ts | 16 +++-- .../common/extensionManagement.ts | 11 ++- .../extensionManagementServerService.ts | 4 +- .../common/extensionManagementService.ts | 4 +- .../profileAwareExtensionManagementService.ts | 57 +++++++++++++++ .../common/webExtensionManagementService.ts | 9 ++- .../extensionManagementServerService.ts | 24 +++---- .../remoteExtensionManagementService.ts | 8 ++- .../extensionEnablementService.test.ts | 11 +-- .../extensions/browser/extensionService.ts | 5 +- .../common/abstractExtensionService.ts | 8 ++- .../electronExtensionService.ts | 6 +- .../browser/userDataProfileManagement.ts | 71 ++++++++++++------- .../userDataProfile/common/userDataProfile.ts | 9 ++- .../common/userDataProfileService.ts | 16 +++-- .../abstractWorkspaceEditingService.ts | 7 +- .../browser/workspaceEditingService.ts | 4 +- .../workspaces/common/workspaceTrust.ts | 4 +- .../workspaceEditingService.ts | 4 +- .../test/common/workspaceTrust.test.ts | 2 +- 32 files changed, 304 insertions(+), 152 deletions(-) create mode 100644 src/vs/workbench/services/extensionManagement/common/profileAwareExtensionManagementService.ts diff --git a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts index 2fa48c8f532..894841a4a4b 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts @@ -10,7 +10,7 @@ import { cloneAndChange } from 'vs/base/common/objects'; import { URI, UriComponents } from 'vs/base/common/uri'; import { DefaultURITransformer, IURITransformer, transformAndReviveIncomingURIs } from 'vs/base/common/uriIpc'; import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { DidUninstallExtensionEvent, IExtensionIdentifier, IExtensionManagementService, IExtensionTipsService, IGalleryExtension, IGalleryMetadata, ILocalExtension, InstallExtensionEvent, InstallExtensionResult, InstallOptions, InstallVSIXOptions, IExtensionsControlManifest, isTargetPlatformCompatible, UninstallOptions, IServerExtensionManagementService, ServerInstallOptions, ServerInstallVSIXOptions, ServerUninstallOptions, Metadata } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { DidUninstallExtensionEvent, IExtensionIdentifier, IExtensionManagementService, IExtensionTipsService, IGalleryExtension, IGalleryMetadata, ILocalExtension, InstallExtensionEvent, InstallExtensionResult, IExtensionsControlManifest, isTargetPlatformCompatible, IServerExtensionManagementService, ServerInstallOptions, ServerInstallVSIXOptions, ServerUninstallOptions, Metadata } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionType, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions'; function transformIncomingURI(uri: UriComponents, transformer: IURITransformer | null): URI { @@ -134,31 +134,28 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt return Promise.resolve(this.channel.call('unzip', [zipLocation])); } - install(vsix: URI, options?: InstallVSIXOptions): Promise { - const serverInstallOptions: ServerInstallVSIXOptions = { ...options, profileLocation: this.getExtensionsProfileResource() }; - return Promise.resolve(this.channel.call('install', [vsix, serverInstallOptions])).then(local => transformIncomingExtension(local, null)); + install(vsix: URI, options?: ServerInstallVSIXOptions): Promise { + return Promise.resolve(this.channel.call('install', [vsix, options])).then(local => transformIncomingExtension(local, null)); } getManifest(vsix: URI): Promise { return Promise.resolve(this.channel.call('getManifest', [vsix])); } - installFromGallery(extension: IGalleryExtension, installOptions?: InstallOptions): Promise { - const serverInstallOptions: ServerInstallOptions = { ...installOptions, profileLocation: this.getExtensionsProfileResource() }; - return Promise.resolve(this.channel.call('installFromGallery', [extension, serverInstallOptions])).then(local => transformIncomingExtension(local, null)); + installFromGallery(extension: IGalleryExtension, installOptions?: ServerInstallOptions): Promise { + return Promise.resolve(this.channel.call('installFromGallery', [extension, installOptions])).then(local => transformIncomingExtension(local, null)); } - uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise { - const serverUninstallOptions: ServerUninstallOptions = { ...options, profileLocation: this.getExtensionsProfileResource() }; - return Promise.resolve(this.channel.call('uninstall', [extension!, serverUninstallOptions])); + uninstall(extension: ILocalExtension, options?: ServerUninstallOptions): Promise { + return Promise.resolve(this.channel.call('uninstall', [extension!, options])); } reinstallFromGallery(extension: ILocalExtension): Promise { return Promise.resolve(this.channel.call('reinstallFromGallery', [extension])); } - getInstalled(type: ExtensionType | null = null): Promise { - return Promise.resolve(this.channel.call('getInstalled', [type, this.getExtensionsProfileResource()])) + getInstalled(type: ExtensionType | null = null, extensionsProfileResource?: URI): Promise { + return Promise.resolve(this.channel.call('getInstalled', [type, extensionsProfileResource])) .then(extensions => extensions.map(extension => transformIncomingExtension(extension, null))); } @@ -181,10 +178,6 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt } registerParticipant() { throw new Error('Not Supported'); } - - protected getExtensionsProfileResource(): URI | undefined { - return undefined; - } } export class ExtensionTipsChannel implements IServerChannel { diff --git a/src/vs/platform/userDataProfile/common/userDataProfile.ts b/src/vs/platform/userDataProfile/common/userDataProfile.ts index 2991a67cdbb..c10d6c5de5c 100644 --- a/src/vs/platform/userDataProfile/common/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/common/userDataProfile.ts @@ -47,6 +47,8 @@ export interface IUserDataProfile { readonly extensionsResource: URI | undefined; } +export type CustomUserDataProfile = IUserDataProfile & { readonly extensionsResource: URI; readonly isDefault: false }; + export function isUserDataProfile(thing: unknown): thing is IUserDataProfile { const candidate = thing as IUserDataProfile | undefined; @@ -74,7 +76,7 @@ export interface IUserDataProfilesService { readonly onDidChangeProfiles: Event; readonly profiles: IUserDataProfile[]; - newProfile(name: string, options?: ProfileOptions): IUserDataProfile; + newProfile(name: string, options?: ProfileOptions): CustomUserDataProfile; createProfile(profile: IUserDataProfile, options: ProfileOptions, workspaceIdentifier?: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise; setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise; getProfile(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): IUserDataProfile; @@ -96,6 +98,10 @@ export function reviveProfile(profile: UriDto, scheme: string) }; } +export const EXTENSIONS_RESOURCE_NAME = 'extensions.json'; + +export function toUserDataProfile(name: string, location: URI, options: ProfileOptions, defaultProfile: true): IUserDataProfile; +export function toUserDataProfile(name: string, location: URI, options: ProfileOptions, defaultProfile: IUserDataProfile): CustomUserDataProfile; export function toUserDataProfile(name: string, location: URI, options: ProfileOptions, defaultProfile: true | IUserDataProfile): IUserDataProfile { return { id: hash(location.toString()).toString(16), @@ -107,7 +113,7 @@ export function toUserDataProfile(name: string, location: URI, options: ProfileO keybindingsResource: defaultProfile === true || options.keybindings ? joinPath(location, 'keybindings.json') : defaultProfile.keybindingsResource, tasksResource: defaultProfile === true || options.tasks ? joinPath(location, 'tasks.json') : defaultProfile.tasksResource, snippetsHome: defaultProfile === true || options.snippets ? joinPath(location, 'snippets') : defaultProfile.snippetsHome, - extensionsResource: defaultProfile === true && !options.extensions ? undefined : joinPath(location, 'extensions.json'), + extensionsResource: defaultProfile === true && !options.extensions ? undefined : joinPath(location, EXTENSIONS_RESOURCE_NAME), }; } @@ -135,7 +141,7 @@ export class UserDataProfilesService extends Disposable implements IUserDataProf this._defaultProfile = defaultProfile ? reviveProfile(defaultProfile, this.profilesHome.scheme) : toUserDataProfile(localize('defaultProfile', "Default"), environmentService.userRoamingDataHome, { ...DefaultOptions, extensions: false }, true); } - newProfile(name: string, options: ProfileOptions = DefaultOptions): IUserDataProfile { + newProfile(name: string, options: ProfileOptions = DefaultOptions): CustomUserDataProfile { return toUserDataProfile(name, joinPath(this.profilesHome, hash(name).toString(16)), options, this.defaultProfile); } diff --git a/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts b/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts index 53b08b880cd..fd7b93059dc 100644 --- a/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts @@ -62,7 +62,7 @@ export class UserDataProfilesMainService extends UserDataProfilesService impleme private _profilesObject: UserDataProfilesObject | undefined; private get profilesObject(): UserDataProfilesObject { if (!this._profilesObject) { - const profiles = this.storedProfiles.map(storedProfile => toUserDataProfile(storedProfile.name, storedProfile.location, storedProfile.options, this.defaultProfile)); + const profiles = this.storedProfiles.map(storedProfile => toUserDataProfile(storedProfile.name, storedProfile.location, storedProfile.options, this.defaultProfile)); profiles.unshift(this.defaultProfile); const workspaces = this.storedWorskpaceInfos.reduce((workspaces, workspaceProfileInfo) => { const profile = profiles.find(p => this.uriIdentityService.extUri.isEqual(p.location, workspaceProfileInfo.profile)); diff --git a/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts b/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts index 823180e0785..3c5e5e0d3ef 100644 --- a/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts @@ -31,7 +31,7 @@ export class UserDataProfilesNativeService extends UserDataProfilesService imple const result = await this.channel.call[]>('getAllProfiles'); this._profiles = result.map(profile => reviveProfile(profile, this.profilesHome.scheme)); this._register(this.channel.listen('onDidChangeProfiles')((profiles) => { - this._profiles = result.map(profile => reviveProfile(profile, this.profilesHome.scheme)); + this._profiles = profiles.map(profile => reviveProfile(profile, this.profilesHome.scheme)); this._onDidChangeProfiles.fire(this._profiles); })); } diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index c6e8fae126b..fa48267fa04 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -554,6 +554,9 @@ export class ActivitybarPart extends Part implements IPaneCompositeSelectorPart } private toggleAccountsActivity() { + if (!!this.accountsActivityAction === this.accountsVisibilityPreference) { + return; + } if (this.globalActivityActionBar) { if (this.accountsActivityAction) { this.globalActivityActionBar.pull(ActivitybarPart.ACCOUNTS_ACTION_INDEX); @@ -837,7 +840,13 @@ export class ActivitybarPart extends Part implements IPaneCompositeSelectorPart for (let index = 0; index < compositeItems.length; index++) { // Add items currently exists but does not exist in new. if (!newCompositeItems.some(({ id }) => id === compositeItems[index].id)) { - newCompositeItems.splice(index, 0, compositeItems[index]); + const viewContainer = this.viewDescriptorService.getViewContainerById(compositeItems[index].id); + newCompositeItems.splice(index, 0, { + ...compositeItems[index], + pinned: true, + visible: true, + order: viewContainer?.order, + }); } } 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 6cbe2bd6ebd..ad2e86b38ba 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 @@ -12,7 +12,7 @@ import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, InstallOperation, IExtensionTipsService, IGalleryMetadata, InstallExtensionResult, getTargetPlatform, IExtensionsControlManifest } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, ExtensionInstallLocation } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, ExtensionInstallLocation, IProfileAwareExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test'; @@ -99,6 +99,7 @@ async function setupTest() { onDidInstallExtensions: didInstallEvent.event, onUninstallExtension: uninstallEvent.event, onDidUninstallExtension: didUninstallEvent.event, + onDidChangeProfileExtensions: Event.None, async getInstalled() { return []; }, async getExtensionsControlManifest() { return { malicious: [], deprecated: {} }; }, async updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata) { @@ -113,7 +114,7 @@ async function setupTest() { instantiationService.stub(IRemoteAgentService, RemoteAgentService); - const localExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', id: 'vscode-local' }; + const localExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService) as IProfileAwareExtensionManagementService, label: 'local', id: 'vscode-local' }; instantiationService.stub(IExtensionManagementServerService, >{ get localExtensionManagementServer(): IExtensionManagementServer { return localExtensionManagementServer; @@ -1543,7 +1544,7 @@ suite('RemoteInstallAction', () => { test('Test remote install action when installing local workspace extension', async () => { // multi server setup - const remoteExtensionManagementService: IExtensionManagementService = createExtensionManagementService(); + const remoteExtensionManagementService = createExtensionManagementService(); const onInstallExtension = new Emitter(); remoteExtensionManagementService.onInstallExtension = onInstallExtension.event; const localWorkspaceExtension = aLocalExtension('a', { extensionKind: ['workspace'] }, { location: URI.file(`pub.a`) }); @@ -1574,7 +1575,7 @@ suite('RemoteInstallAction', () => { test('Test remote install action when installing local workspace extension is finished', async () => { // multi server setup - const remoteExtensionManagementService: IExtensionManagementService = createExtensionManagementService(); + const remoteExtensionManagementService = createExtensionManagementService(); const onInstallExtension = new Emitter(); remoteExtensionManagementService.onInstallExtension = onInstallExtension.event; const onDidInstallEvent = new Emitter(); @@ -1777,7 +1778,7 @@ suite('RemoteInstallAction', () => { test('Test remote install action is disabled for local workspace extension if it is uninstalled locally', async () => { // multi server setup - const extensionManagementService = instantiationService.get(IExtensionManagementService); + const extensionManagementService = instantiationService.get(IExtensionManagementService) as IProfileAwareExtensionManagementService; const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, extensionManagementService); instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); @@ -1921,7 +1922,7 @@ suite('RemoteInstallAction', () => { test('Test remote install action is disabled if local language pack extension is uninstalled', async () => { // multi server setup - const extensionManagementService = instantiationService.get(IExtensionManagementService); + const extensionManagementService = instantiationService.get(IExtensionManagementService) as IProfileAwareExtensionManagementService; const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, extensionManagementService); instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); @@ -1994,7 +1995,7 @@ suite('LocalInstallAction', () => { test('Test local install action when installing remote ui extension', async () => { // multi server setup - const localExtensionManagementService: IExtensionManagementService = createExtensionManagementService(); + const localExtensionManagementService = createExtensionManagementService(); const onInstallExtension = new Emitter(); localExtensionManagementService.onInstallExtension = onInstallExtension.event; const remoteUIExtension = aLocalExtension('a', { extensionKind: ['ui'] }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); @@ -2025,7 +2026,7 @@ suite('LocalInstallAction', () => { test('Test local install action when installing remote ui extension is finished', async () => { // multi server setup - const localExtensionManagementService: IExtensionManagementService = createExtensionManagementService(); + const localExtensionManagementService = createExtensionManagementService(); const onInstallExtension = new Emitter(); localExtensionManagementService.onInstallExtension = onInstallExtension.event; const onDidInstallEvent = new Emitter(); @@ -2188,7 +2189,7 @@ suite('LocalInstallAction', () => { test('Test local install action is disabled for remoteUI extension if it is uninstalled locally', async () => { // multi server setup - const extensionManagementService = instantiationService.get(IExtensionManagementService); + const extensionManagementService = instantiationService.get(IExtensionManagementService) as IProfileAwareExtensionManagementService; const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService(), extensionManagementService); instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); @@ -2311,7 +2312,7 @@ suite('LocalInstallAction', () => { test('Test local install action is disabled if remote language pack extension is uninstalled', async () => { // multi server setup - const extensionManagementService = instantiationService.get(IExtensionManagementService); + const extensionManagementService = instantiationService.get(IExtensionManagementService) as IProfileAwareExtensionManagementService; const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService(), extensionManagementService); instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); @@ -2362,7 +2363,7 @@ function aPage(...objects: T[]): IPager { return { firstPage: objects, total: objects.length, pageSize: objects.length, getPage: () => null! }; } -function aSingleRemoteExtensionManagementServerService(instantiationService: TestInstantiationService, remoteExtensionManagementService?: IExtensionManagementService): IExtensionManagementServerService { +function aSingleRemoteExtensionManagementServerService(instantiationService: TestInstantiationService, remoteExtensionManagementService?: IProfileAwareExtensionManagementService): IExtensionManagementServerService { const remoteExtensionManagementServer: IExtensionManagementServer = { id: 'vscode-remote', label: 'remote', @@ -2386,7 +2387,7 @@ function aSingleRemoteExtensionManagementServerService(instantiationService: Tes }; } -function aMultiExtensionManagementServerService(instantiationService: TestInstantiationService, localExtensionManagementService?: IExtensionManagementService, remoteExtensionManagementService?: IExtensionManagementService): IExtensionManagementServerService { +function aMultiExtensionManagementServerService(instantiationService: TestInstantiationService, localExtensionManagementService?: IProfileAwareExtensionManagementService, remoteExtensionManagementService?: IProfileAwareExtensionManagementService): IExtensionManagementServerService { const localExtensionManagementServer: IExtensionManagementServer = { id: 'vscode-local', label: 'local', @@ -2418,12 +2419,13 @@ function aMultiExtensionManagementServerService(instantiationService: TestInstan }; } -function createExtensionManagementService(installed: ILocalExtension[] = []): IExtensionManagementService { - return { +function createExtensionManagementService(installed: ILocalExtension[] = []): IProfileAwareExtensionManagementService { + return { onInstallExtension: Event.None, onDidInstallExtensions: Event.None, onUninstallExtension: Event.None, onDidUninstallExtension: Event.None, + onDidChangeProfileExtensions: Event.None, getInstalled: () => Promise.resolve(installed), canInstall: async (extension: IGalleryExtension) => { return true; }, installFromGallery: (extension: IGalleryExtension) => Promise.reject(new Error('not supported')), 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 1212a0ed3b2..f7b374b3387 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 @@ -13,7 +13,7 @@ import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, SortBy, InstallExtensionResult, getTargetPlatform, IExtensionInfo } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IProfileAwareExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IExtensionRecommendationsService, ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test'; @@ -96,6 +96,7 @@ suite('ExtensionsListView Tests', () => { onDidInstallExtensions: didInstallEvent.event, onUninstallExtension: uninstallEvent.event, onDidUninstallExtension: didUninstallEvent.event, + onDidChangeProfileExtensions: Event.None, async getInstalled() { return []; }, async canInstall() { return true; }, async getExtensionsControlManifest() { return { malicious: [], deprecated: {} }; }, @@ -105,7 +106,7 @@ suite('ExtensionsListView Tests', () => { instantiationService.stub(IContextKeyService, new MockContextKeyService()); instantiationService.stub(IMenuService, new TestMenuService()); - const localExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', id: 'vscode-local' }; + const localExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService) as IProfileAwareExtensionManagementService, label: 'local', id: 'vscode-local' }; instantiationService.stub(IExtensionManagementServerService, >{ get localExtensionManagementServer(): IExtensionManagementServer { return localExtensionManagementServer; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index 70f286ce690..018ad78c48f 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -13,7 +13,7 @@ import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, DidUninstallExtensionEvent, InstallExtensionEvent, IGalleryExtensionAssets, IExtensionIdentifier, InstallOperation, IExtensionTipsService, IGalleryMetadata, InstallExtensionResult, getTargetPlatform, IExtensionsControlManifest } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IProfileAwareExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { anExtensionManagementServerService, TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test'; @@ -94,6 +94,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { onDidInstallExtensions: didInstallEvent.event, onUninstallExtension: uninstallEvent.event, onDidUninstallExtension: didUninstallEvent.event, + onDidChangeProfileExtensions: Event.None, async getInstalled() { return []; }, async getExtensionsControlManifest() { return { malicious: [], deprecated: {} }; }, async updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata) { @@ -109,7 +110,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService({ id: 'local', label: 'local', - extensionManagementService: instantiationService.get(IExtensionManagementService), + extensionManagementService: instantiationService.get(IExtensionManagementService) as IProfileAwareExtensionManagementService, }, null, null)); instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); @@ -1458,7 +1459,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); } - function aMultiExtensionManagementServerService(instantiationService: TestInstantiationService, localExtensionManagementService?: IExtensionManagementService, remoteExtensionManagementService?: IExtensionManagementService): IExtensionManagementServerService { + function aMultiExtensionManagementServerService(instantiationService: TestInstantiationService, localExtensionManagementService?: IProfileAwareExtensionManagementService, remoteExtensionManagementService?: IProfileAwareExtensionManagementService): IExtensionManagementServerService { const localExtensionManagementServer: IExtensionManagementServer = { id: 'vscode-local', label: 'local', @@ -1472,12 +1473,13 @@ suite('ExtensionsWorkbenchServiceTest', () => { return anExtensionManagementServerService(localExtensionManagementServer, remoteExtensionManagementServer, null); } - function createExtensionManagementService(installed: ILocalExtension[] = []): IExtensionManagementService { - return { + function createExtensionManagementService(installed: ILocalExtension[] = []): IProfileAwareExtensionManagementService { + return { onInstallExtension: Event.None, onDidInstallExtensions: Event.None, onUninstallExtension: Event.None, onDidUninstallExtension: Event.None, + onDidChangeProfileExtensions: Event.None, getInstalled: () => Promise.resolve(installed), installFromGallery: (extension: IGalleryExtension) => Promise.reject(new Error('not supported')), updateMetadata: async (local: ILocalExtension, metadata: IGalleryMetadata) => { diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts index 1b94b3fe090..2f7a8569e42 100644 --- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Event } from 'vs/base/common/event'; import { Disposable, DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { localize } from 'vs/nls'; @@ -13,7 +14,7 @@ import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userData import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { WorkbenchStateContext } from 'vs/workbench/common/contextkeys'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar'; +import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar'; import { IUserDataProfileManagementService, IUserDataProfileService, ManageProfilesSubMenu, PROFILES_CATEGORY, PROFILES_TTILE } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; const CONTEXT_CURRENT_PROFILE = new RawContextKey('currentUserDataProfile', ''); @@ -36,7 +37,7 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements this.currentProfileContext.set(this.userDataProfileService.currentProfile.id); this.updateStatus(); - this._register(this.workspaceContextService.onDidChangeWorkbenchState(() => this.updateStatus())); + this._register(Event.any(this.workspaceContextService.onDidChangeWorkbenchState, this.userDataProfileService.onDidChangeCurrentProfile, this.userDataProfilesService.onDidChangeProfiles)(() => this.updateStatus())); this.registerActions(); } @@ -106,14 +107,25 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements }); } - private async updateStatus(): Promise { + private profileStatusAccessor: IStatusbarEntryAccessor | undefined; + private updateStatus(): void { if (this.userDataProfilesService.profiles.length > 1 && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY) { - this.statusBarService.addEntry({ + const statusBarEntry = { name: this.userDataProfileService.currentProfile.name!, command: 'workbench.profiles.actions.switchProfile', ariaLabel: localize('currentProfile', "Current Settings Profile is {0}", this.userDataProfileService.currentProfile.name), text: `${PROFILES_CATEGORY}: ${this.userDataProfileService.currentProfile.name!}`, - }, 'status.userDataProfile', StatusbarAlignment.LEFT, 1); + }; + if (this.profileStatusAccessor) { + this.profileStatusAccessor.update(statusBarEntry); + } else { + this.profileStatusAccessor = this.statusBarService.addEntry(statusBarEntry, 'status.userDataProfile', StatusbarAlignment.LEFT, 1); + } + } else { + if (this.profileStatusAccessor) { + this.profileStatusAccessor.dispose(); + this.profileStatusAccessor = undefined; + } } } } diff --git a/src/vs/workbench/services/configuration/browser/configuration.ts b/src/vs/workbench/services/configuration/browser/configuration.ts index 472a47705b8..6039018ed45 100644 --- a/src/vs/workbench/services/configuration/browser/configuration.ts +++ b/src/vs/workbench/services/configuration/browser/configuration.ts @@ -26,8 +26,8 @@ import { joinPath } from 'vs/base/common/resources'; import { Registry } from 'vs/platform/registry/common/platform'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { isObject } from 'vs/base/common/types'; -import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { DefaultConfiguration as BaseDefaultConfiguration } from 'vs/platform/configuration/common/configurations'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; export class DefaultConfiguration extends BaseDefaultConfiguration { @@ -120,7 +120,8 @@ export class UserConfiguration extends Disposable { private readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; - private readonly userConfiguration: MutableDisposable = this._register(new MutableDisposable()); + private readonly userConfiguration = this._register(new MutableDisposable()); + private readonly userConfigurationChangeDisposable = this._register(new MutableDisposable()); private readonly reloadConfigurationScheduler: RunOnceScheduler; private readonly configurationParseOptions: ConfigurationParseOptions; @@ -128,19 +129,40 @@ export class UserConfiguration extends Disposable { get hasTasksLoaded(): boolean { return this.userConfiguration.value instanceof FileServiceBasedConfiguration; } constructor( - private readonly userDataProfile: IUserDataProfile, scopes: ConfigurationScope[] | undefined, + private readonly userDataProfileService: IUserDataProfileService, private readonly fileService: IFileService, private readonly uriIdentityService: IUriIdentityService, private readonly logService: ILogService, ) { super(); this.configurationParseOptions = { scopes, skipRestricted: false }; - this.userConfiguration.value = new UserSettings(this.userDataProfile.settingsResource, scopes, uriIdentityService.extUri, this.fileService); - this._register(this.userConfiguration.value.onDidChange(() => this.reloadConfigurationScheduler.schedule())); + this.userConfiguration.value = new UserSettings(this.userDataProfileService.currentProfile.settingsResource, scopes, uriIdentityService.extUri, this.fileService); + this._register(this.userDataProfileService.onDidChangeCurrentProfile(e => e.join(this.onDidChangeCurrentProfile()))); + this.userConfigurationChangeDisposable.value = this.userConfiguration.value.onDidChange(() => this.reloadConfigurationScheduler.schedule()); this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reload().then(configurationModel => this._onDidChangeConfiguration.fire(configurationModel)), 50)); } + private async onDidChangeCurrentProfile(): Promise { + await this.resetUserConfiguration(); + this.reloadConfigurationScheduler.schedule(); + } + + private async resetUserConfiguration(): Promise { + const folder = this.uriIdentityService.extUri.dirname(this.userDataProfileService.currentProfile.settingsResource); + const standAloneConfigurationResources: [string, URI][] = [[TASKS_CONFIGURATION_KEY, this.userDataProfileService.currentProfile.tasksResource]]; + const fileServiceBasedConfiguration = new FileServiceBasedConfiguration(folder.toString(), this.userDataProfileService.currentProfile.settingsResource, standAloneConfigurationResources, this.configurationParseOptions, this.fileService, this.uriIdentityService, this.logService); + const configurationModel = await fileServiceBasedConfiguration.loadConfiguration(); + this.userConfiguration.value = fileServiceBasedConfiguration; + + // Check for value because userConfiguration might have been disposed. + if (this.userConfigurationChangeDisposable.value) { + this.userConfigurationChangeDisposable.value = this.userConfiguration.value.onDidChange(() => this.reloadConfigurationScheduler.schedule()); + } + + return configurationModel; + } + async initialize(): Promise { return this.userConfiguration.value!.loadConfiguration(); } @@ -149,19 +171,7 @@ export class UserConfiguration extends Disposable { if (this.hasTasksLoaded) { return this.userConfiguration.value!.loadConfiguration(); } - - const folder = this.uriIdentityService.extUri.dirname(this.userDataProfile.settingsResource); - const standAloneConfigurationResources: [string, URI][] = [[TASKS_CONFIGURATION_KEY, this.userDataProfile.tasksResource]]; - const fileServiceBasedConfiguration = new FileServiceBasedConfiguration(folder.toString(), this.userDataProfile.settingsResource, standAloneConfigurationResources, this.configurationParseOptions, this.fileService, this.uriIdentityService, this.logService); - const configurationModel = await fileServiceBasedConfiguration.loadConfiguration(); - this.userConfiguration.value = fileServiceBasedConfiguration; - - // Check for value because userConfiguration might have been disposed. - if (this.userConfiguration.value) { - this._register(this.userConfiguration.value.onDidChange(() => this.reloadConfigurationScheduler.schedule())); - } - - return configurationModel; + return this.resetUserConfiguration(); } reparse(): ConfigurationModel { diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 24d461f8e3c..0d227fc057d 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -123,7 +123,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat this.logService = logService; this._configuration = new Configuration(this.defaultConfiguration.configurationModel, this.policyConfiguration.configurationModel, new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), this.workspace); this.cachedFolderConfigs = new ResourceMap(); - this.localUserConfiguration = this._register(new UserConfiguration(userDataProfileService.currentProfile, remoteAuthority ? LOCAL_MACHINE_SCOPES : undefined, fileService, uriIdentityService, logService)); + this.localUserConfiguration = this._register(new UserConfiguration(remoteAuthority ? LOCAL_MACHINE_SCOPES : undefined, userDataProfileService, fileService, uriIdentityService, logService)); this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration))); if (remoteAuthority) { const remoteUserConfiguration = this.remoteUserConfiguration = this._register(new RemoteUserConfiguration(remoteAuthority, configurationCache, fileService, uriIdentityService, remoteAgentService)); diff --git a/src/vs/workbench/services/configuration/common/configuration.ts b/src/vs/workbench/services/configuration/common/configuration.ts index c3d97f0c5e6..311dd5ab4f5 100644 --- a/src/vs/workbench/services/configuration/common/configuration.ts +++ b/src/vs/workbench/services/configuration/common/configuration.ts @@ -9,6 +9,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { refineServiceDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Event } from 'vs/base/common/event'; import { ResourceMap } from 'vs/base/common/map'; +import { IAnyWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export const FOLDER_CONFIG_FOLDER_NAME = '.vscode'; export const FOLDER_SETTINGS_NAME = 'settings'; @@ -72,6 +73,12 @@ export interface IWorkbenchConfigurationService extends IConfigurationService { * The promise is resolved immediately if the window is not remote. */ whenRemoteConfigurationLoaded(): Promise; + + /** + * Initialize configuration service for the given workspace + * @param arg workspace Identifier + */ + initialize(arg: IAnyWorkspaceIdentifier): Promise; } export const TASKS_DEFAULT = '{\n\t\"version\": \"2.0.0\",\n\t\"tasks\": []\n}'; diff --git a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts index e25063c3b34..264a59acbc7 100644 --- a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts @@ -652,7 +652,8 @@ class ExtensionsManager extends Disposable { this.logService.error(error); } this._register(this.extensionManagementService.onDidInstallExtensions(e => this.onDidInstallExtensions(e.reduce((result, { local, operation }) => { if (local && operation !== InstallOperation.Migrate) { result.push(local); } return result; }, [])))); - this._register(Event.filter(this.extensionManagementService.onDidUninstallExtension, (e => !e.error))(e => this.onDidUninstallExtension(e.identifier, e.server))); + this._register(Event.filter(this.extensionManagementService.onDidUninstallExtension, (e => !e.error))(e => this.onDidUninstallExtensions([e.identifier], e.server))); + this._register(this.extensionManagementService.onDidChangeProfileExtensions(({ added, removed, server }) => { this.onDidInstallExtensions(added); this.onDidUninstallExtensions(removed.map(({ identifier }) => identifier), server); })); } private onDidInstallExtensions(extensions: IExtension[]): void { @@ -662,10 +663,15 @@ class ExtensionsManager extends Disposable { } } - private onDidUninstallExtension(identifier: IExtensionIdentifier, server: IExtensionManagementServer): void { - const index = this._extensions.findIndex(e => areSameExtensions(e.identifier, identifier) && this.extensionManagementServerService.getExtensionManagementServer(e) === server); - if (index !== -1) { - const removed = this._extensions.splice(index, 1); + private onDidUninstallExtensions(identifiers: IExtensionIdentifier[], server: IExtensionManagementServer): void { + const removed: IExtension[] = []; + for (const identifier of identifiers) { + const index = this._extensions.findIndex(e => areSameExtensions(e.identifier, identifier) && this.extensionManagementServerService.getExtensionManagementServer(e) === server); + if (index !== -1) { + removed.push(...this._extensions.splice(index, 1)); + } + } + if (removed.length) { this._onDidChangeExtensions.fire({ added: [], removed }); } } diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts index 7e8d96d0bb6..66f0b79da91 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts @@ -10,10 +10,17 @@ import { IExtensionManagementService, IGalleryExtension, IExtensionIdentifier, I import { URI } from 'vs/base/common/uri'; import { FileAccess } from 'vs/base/common/network'; +export type DidChangeProfileExtensionsEvent = { readonly added: ILocalExtension[]; readonly removed: ILocalExtension[] }; + +export interface IProfileAwareExtensionManagementService extends IExtensionManagementService { + onDidChangeProfileExtensions: Event; + switchExtensionsProfile(extensionsProfileResource: URI | undefined): Promise; +} + export interface IExtensionManagementServer { readonly id: string; readonly label: string; - readonly extensionManagementService: IExtensionManagementService; + readonly extensionManagementService: IProfileAwareExtensionManagementService; } export const enum ExtensionInstallLocation { @@ -37,6 +44,7 @@ export const DefaultIconPath = FileAccess.asBrowserUri('./media/defaultIcon.png' export type InstallExtensionOnServerEvent = InstallExtensionEvent & { server: IExtensionManagementServer }; export type UninstallExtensionOnServerEvent = IExtensionIdentifier & { server: IExtensionManagementServer }; export type DidUninstallExtensionOnServerEvent = DidUninstallExtensionEvent & { server: IExtensionManagementServer }; +export type DidChangeProfileExtensionsOnServerEvent = DidChangeProfileExtensionsEvent & { server: IExtensionManagementServer }; export const IWorkbenchExtensionManagementService = refineServiceDecorator(IExtensionManagementService); export interface IWorkbenchExtensionManagementService extends IExtensionManagementService { @@ -46,6 +54,7 @@ export interface IWorkbenchExtensionManagementService extends IExtensionManageme onDidInstallExtensions: Event; onUninstallExtension: Event; onDidUninstallExtension: Event; + onDidChangeProfileExtensions: Event; installVSIX(location: URI, manifest: IExtensionManifest, installOptions?: InstallVSIXOptions): Promise; installWebExtension(location: URI): Promise; diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts index 5622a25d528..233e0bc56c2 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts @@ -14,7 +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 { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; +import { NativeProfileAwareExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/profileAwareExtensionManagementService'; 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(NativeProfileAwareExtensionManagementService, remoteAgentConnection.getChannel('extensions'), undefined); 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 e50218a6c6a..1205ab9b412 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts @@ -7,7 +7,7 @@ import { Event, EventMultiplexer } from 'vs/base/common/event'; import { ILocalExtension, IGalleryExtension, IExtensionIdentifier, IExtensionsControlManifest, IGalleryMetadata, IExtensionGalleryService, InstallOptions, UninstallOptions, InstallVSIXOptions, InstallExtensionResult, ExtensionManagementError, ExtensionManagementErrorCode, Metadata } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { DidUninstallExtensionOnServerEvent, IExtensionManagementServer, IExtensionManagementServerService, InstallExtensionOnServerEvent, IWorkbenchExtensionManagementService, UninstallExtensionOnServerEvent } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { DidChangeProfileExtensionsOnServerEvent, DidUninstallExtensionOnServerEvent, IExtensionManagementServer, IExtensionManagementServerService, InstallExtensionOnServerEvent, IWorkbenchExtensionManagementService, UninstallExtensionOnServerEvent } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ExtensionType, isLanguagePackExtension, IExtensionManifest, getWorkspaceSupportTypeMessage, TargetPlatform } from 'vs/platform/extensions/common/extensions'; import { URI } from 'vs/base/common/uri'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -40,6 +40,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench readonly onDidInstallExtensions: Event; readonly onUninstallExtension: Event; readonly onDidUninstallExtension: Event; + readonly onDidChangeProfileExtensions: Event; protected readonly servers: IExtensionManagementServer[] = []; @@ -72,6 +73,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench this.onDidInstallExtensions = this._register(this.servers.reduce((emitter: EventMultiplexer, server) => { emitter.add(server.extensionManagementService.onDidInstallExtensions); return emitter; }, new EventMultiplexer())).event; this.onUninstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer, server) => { emitter.add(Event.map(server.extensionManagementService.onUninstallExtension, e => ({ ...e, server }))); return emitter; }, new EventMultiplexer())).event; this.onDidUninstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer, server) => { emitter.add(Event.map(server.extensionManagementService.onDidUninstallExtension, e => ({ ...e, server }))); return emitter; }, new EventMultiplexer())).event; + this.onDidChangeProfileExtensions = this._register(this.servers.reduce((emitter: EventMultiplexer, server) => { emitter.add(Event.map(server.extensionManagementService.onDidChangeProfileExtensions, e => ({ ...e, server }))); return emitter; }, new EventMultiplexer())).event; } async getInstalled(type?: ExtensionType): Promise { diff --git a/src/vs/workbench/services/extensionManagement/common/profileAwareExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/profileAwareExtensionManagementService.ts new file mode 100644 index 00000000000..94d70ade4d3 --- /dev/null +++ b/src/vs/workbench/services/extensionManagement/common/profileAwareExtensionManagementService.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 { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IProfileAwareExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; +import { URI } from 'vs/base/common/uri'; +import { IGalleryExtension, ILocalExtension, InstallOptions, InstallVSIXOptions, UninstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ExtensionIdentifier, ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { Emitter } from 'vs/base/common/event'; +import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { delta } from 'vs/base/common/arrays'; +import { compare } from 'vs/base/common/strings'; + +export class NativeProfileAwareExtensionManagementService extends ExtensionManagementChannelClient implements IProfileAwareExtensionManagementService { + + private readonly _onDidChangeProfileExtensions = this._register(new Emitter<{ readonly added: ILocalExtension[]; readonly removed: ILocalExtension[] }>()); + readonly onDidChangeProfileExtensions = this._onDidChangeProfileExtensions.event; + + constructor(channel: IChannel, private extensionsProfileResource: URI | undefined, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, + ) { + super(channel); + } + + override install(vsix: URI, options?: InstallVSIXOptions): Promise { + return super.install(vsix, { ...options, profileLocation: this.extensionsProfileResource }); + } + + override installFromGallery(extension: IGalleryExtension, installOptions?: InstallOptions): Promise { + return super.installFromGallery(extension, { ...installOptions, profileLocation: this.extensionsProfileResource }); + } + + override uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise { + return super.uninstall(extension, { ...options, profileLocation: this.extensionsProfileResource }); + } + + override getInstalled(type: ExtensionType | null = null): Promise { + return super.getInstalled(type, this.extensionsProfileResource); + } + + async switchExtensionsProfile(extensionsProfileResource: URI | undefined): Promise { + if (this.uriIdentityService.extUri.isEqual(extensionsProfileResource, this.extensionsProfileResource)) { + return; + } + const oldExtensions = await this.getInstalled(ExtensionType.User); + this.extensionsProfileResource = extensionsProfileResource; + const newExtensions = await this.getInstalled(ExtensionType.User); + const { added, removed } = delta(oldExtensions, newExtensions, (a, b) => compare(ExtensionIdentifier.toKey(a.identifier.id), ExtensionIdentifier.toKey(b.identifier.id))); + if (added.length || removed.length) { + this._onDidChangeProfileExtensions.fire({ added, removed }); + } + } + +} diff --git a/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts index 8643e20e99d..7bc61e1b482 100644 --- a/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts @@ -6,8 +6,9 @@ import { ExtensionType, IExtension, IExtensionIdentifier, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions'; import { IExtensionManagementService, ILocalExtension, IGalleryExtension, IGalleryMetadata, InstallOperation, IExtensionGalleryService, InstallOptions, Metadata } from 'vs/platform/extensionManagement/common/extensionManagement'; import { URI } from 'vs/base/common/uri'; +import { Event } from 'vs/base/common/event'; import { areSameExtensions, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { IScannedExtension, IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IProfileAwareExtensionManagementService, IScannedExtension, IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; import { AbstractExtensionManagementService, AbstractExtensionTask, IInstallExtensionTask, IUninstallExtensionTask, UninstallExtensionTaskOptions } from 'vs/platform/extensionManagement/common/abstractExtensionManagementService'; @@ -17,10 +18,12 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { isBoolean, isUndefined } from 'vs/base/common/types'; import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; -export class WebExtensionManagementService extends AbstractExtensionManagementService implements IExtensionManagementService { +export class WebExtensionManagementService extends AbstractExtensionManagementService implements IExtensionManagementService, IProfileAwareExtensionManagementService { declare readonly _serviceBrand: undefined; + readonly onDidChangeProfileExtensions = Event.None; + constructor( @IExtensionGalleryService extensionGalleryService: IExtensionGalleryService, @ITelemetryService telemetryService: ITelemetryService, @@ -93,6 +96,8 @@ export class WebExtensionManagementService extends AbstractExtensionManagementSe return local; } + async switchExtensionsProfile(extensionsProfileResource: URI | undefined): Promise { } + protected createDefaultInstallExtensionTask(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: InstallOptions): IInstallExtensionTask { return new InstallExtensionTask(manifest, extension, options, this.webExtensionsScannerService); } diff --git a/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService.ts b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService.ts index e8fe314d41b..ece4c6091b0 100644 --- a/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService.ts @@ -14,16 +14,15 @@ import { NativeRemoteExtensionManagementService } from 'vs/workbench/services/ex 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 { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; -import { URI } from 'vs/base/common/uri'; +import { NativeProfileAwareExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/profileAwareExtensionManagementService'; +import { Disposable } from 'vs/base/common/lifecycle'; -export class ExtensionManagementServerService implements IExtensionManagementServerService { +export class ExtensionManagementServerService extends Disposable implements IExtensionManagementServerService { declare readonly _serviceBrand: undefined; - private readonly _localExtensionManagementServer: IExtensionManagementServer; - public get localExtensionManagementServer(): IExtensionManagementServer { return this._localExtensionManagementServer; } + readonly localExtensionManagementServer: IExtensionManagementServer; readonly remoteExtensionManagementServer: IExtensionManagementServer | null = null; readonly webExtensionManagementServer: IExtensionManagementServer | null = null; @@ -34,16 +33,10 @@ export class ExtensionManagementServerService implements IExtensionManagementSer @IUserDataProfileService userDataProfileService: IUserDataProfileService, @IInstantiationService instantiationService: IInstantiationService, ) { - const localExtensionManagementService = new class extends ExtensionManagementChannelClient { - constructor() { - super(sharedProcessService.getChannel('extensions')); - } - protected override getExtensionsProfileResource(): URI | undefined { - return userDataProfileService.currentProfile.extensionsResource; - } - }; - - this._localExtensionManagementServer = { extensionManagementService: localExtensionManagementService, id: 'local', label: localize('local', "Local") }; + super(); + const localExtensionManagementService = this._register(instantiationService.createInstance(NativeProfileAwareExtensionManagementService, sharedProcessService.getChannel('extensions'), userDataProfileService.currentProfile.extensionsResource)); + this.localExtensionManagementServer = { extensionManagementService: localExtensionManagementService, id: 'local', label: localize('local', "Local") }; + this._register(userDataProfileService.onDidChangeCurrentProfile(e => e.join(localExtensionManagementService.switchExtensionsProfile(e.profile.extensionsResource)))); const remoteAgentConnection = remoteAgentService.getConnection(); if (remoteAgentConnection) { const extensionManagementService = instantiationService.createInstance(NativeRemoteExtensionManagementService, remoteAgentConnection.getChannel('extensions'), this.localExtensionManagementServer); @@ -53,6 +46,7 @@ export class ExtensionManagementServerService implements IExtensionManagementSer get label() { return labelService.getHostLabel(Schemas.vscodeRemote, remoteAgentConnection!.remoteAuthority) || localize('remote', "Remote"); }, }; } + } getExtensionManagementServer(extension: IExtension): IExtensionManagementServer { diff --git a/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts index f431cac5378..53c178a68f6 100644 --- a/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts @@ -21,9 +21,10 @@ import { IExtensionManagementServer } from 'vs/workbench/services/extensionManag import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { Promises } from 'vs/base/common/async'; import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; -import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; +import { NativeProfileAwareExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/profileAwareExtensionManagementService'; +import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; -export class NativeRemoteExtensionManagementService extends ExtensionManagementChannelClient implements IExtensionManagementService { +export class NativeRemoteExtensionManagementService extends NativeProfileAwareExtensionManagementService implements IExtensionManagementService { constructor( channel: IChannel, @@ -34,8 +35,9 @@ export class NativeRemoteExtensionManagementService extends ExtensionManagementC @IProductService private readonly productService: IProductService, @INativeWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService, @IExtensionManifestPropertiesService private readonly extensionManifestPropertiesService: IExtensionManifestPropertiesService, + @IUriIdentityService uriIdentityService: IUriIdentityService, ) { - super(channel); + super(channel, undefined, uriIdentityService); } override async install(vsix: URI, options?: InstallVSIXOptions): Promise { 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 660c2fcd2e7..75247ea427d 100644 --- a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts +++ b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts @@ -5,10 +5,10 @@ import * as assert from 'assert'; import * as sinon from 'sinon'; import { IExtensionManagementService, DidUninstallExtensionEvent, ILocalExtension, InstallExtensionEvent, InstallExtensionResult } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IWorkbenchExtensionManagementService, ExtensionInstallLocation } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IWorkbenchExtensionManagementService, ExtensionInstallLocation, IProfileAwareExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ExtensionEnablementService } from 'vs/workbench/services/extensionManagement/browser/extensionEnablementService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { IWorkspace, IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage'; @@ -59,7 +59,7 @@ export class TestExtensionEnablementService extends ExtensionEnablementService { instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService({ id: 'local', label: 'local', - extensionManagementService: { + extensionManagementService: { onInstallExtension: new Emitter().event, onDidInstallExtensions: new Emitter().event, onUninstallExtension: new Emitter().event, @@ -125,9 +125,10 @@ suite('ExtensionEnablementService Test', () => { instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService({ id: 'local', label: 'local', - extensionManagementService: { + extensionManagementService: { onDidInstallExtensions: didInstallEvent.event, onDidUninstallExtension: didUninstallEvent.event, + onDidChangeProfileExtensions: Event.None, getInstalled: () => Promise.resolve(installed) }, }, null, null)); @@ -955,7 +956,7 @@ function anExtensionManagementServer(authority: string, instantiationService: Te return { id: authority, label: authority, - extensionManagementService: instantiationService.get(IExtensionManagementService), + extensionManagementService: instantiationService.get(IExtensionManagementService) as IProfileAwareExtensionManagementService, }; } diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index 1384f29e5ac..c9f5de7f2e4 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IWorkbenchExtensionEnablementService, IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, IWebExtensionsScannerService, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -25,7 +25,6 @@ import { Schemas } from 'vs/base/common/network'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; import { IUserDataInitializationService } from 'vs/workbench/services/userData/browser/userDataInit'; @@ -45,7 +44,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten @IWorkbenchExtensionEnablementService extensionEnablementService: IWorkbenchExtensionEnablementService, @IFileService fileService: IFileService, @IProductService productService: IProductService, - @IExtensionManagementService extensionManagementService: IExtensionManagementService, + @IWorkbenchExtensionManagementService extensionManagementService: IWorkbenchExtensionManagementService, @IWorkspaceContextService contextService: IWorkspaceContextService, @IConfigurationService configurationService: IConfigurationService, @IExtensionManifestPropertiesService extensionManifestPropertiesService: IExtensionManifestPropertiesService, diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index c7610d0bfc2..861e846d9ca 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -11,7 +11,7 @@ import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle' import * as perf from 'vs/base/common/performance'; import { isEqualOrParent } from 'vs/base/common/resources'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IWebExtensionsScannerService, IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWebExtensionsScannerService, IWorkbenchExtensionEnablementService, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; 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'; @@ -25,7 +25,7 @@ import { ExtensionKind } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; import { IProductService } from 'vs/platform/product/common/productService'; -import { IExtensionManagementService, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionActivationHost as IWorkspaceContainsActivationHost, checkGlobFileExists, checkActivateWorkspaceContainsExtension } from 'vs/workbench/services/extensions/common/workspaceContains'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -181,7 +181,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx @IWorkbenchExtensionEnablementService protected readonly _extensionEnablementService: IWorkbenchExtensionEnablementService, @IFileService protected readonly _fileService: IFileService, @IProductService protected readonly _productService: IProductService, - @IExtensionManagementService protected readonly _extensionManagementService: IExtensionManagementService, + @IWorkbenchExtensionManagementService protected readonly _extensionManagementService: IWorkbenchExtensionManagementService, @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, @IConfigurationService protected readonly _configurationService: IConfigurationService, @IExtensionManifestPropertiesService protected readonly _extensionManifestPropertiesService: IExtensionManifestPropertiesService, @@ -235,6 +235,8 @@ export abstract class AbstractExtensionService extends Disposable implements IEx this._handleDeltaExtensions(new DeltaExtensionsQueueItem(toAdd, toRemove)); })); + this._register(this._extensionManagementService.onDidChangeProfileExtensions(({ added, removed }) => this._handleDeltaExtensions(new DeltaExtensionsQueueItem(added, removed)))); + this._register(this._extensionManagementService.onDidInstallExtensions((result) => { const extensions: IExtension[] = []; for (const { local, operation } of result) { diff --git a/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts b/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts index db4ffbea65b..9a989916aa6 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts @@ -8,8 +8,8 @@ import { AbstractExtensionService, ExtensionHostCrashTracker, ExtensionRunningPr import * as nls from 'vs/nls'; import { runWhenIdle } from 'vs/base/common/async'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IWebExtensionsScannerService, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IRemoteExtensionHostDataProvider, RemoteExtensionHost, IRemoteExtensionHostInitData } from 'vs/workbench/services/extensions/common/remoteExtensionHost'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; @@ -66,7 +66,7 @@ export abstract class ElectronExtensionService extends AbstractExtensionService @IWorkbenchExtensionEnablementService extensionEnablementService: IWorkbenchExtensionEnablementService, @IFileService fileService: IFileService, @IProductService productService: IProductService, - @IExtensionManagementService extensionManagementService: IExtensionManagementService, + @IWorkbenchExtensionManagementService extensionManagementService: IWorkbenchExtensionManagementService, @IWorkspaceContextService contextService: IWorkspaceContextService, @IConfigurationService configurationService: IConfigurationService, @IExtensionManifestPropertiesService extensionManifestPropertiesService: IExtensionManifestPropertiesService, diff --git a/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts b/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts index 15085949c96..c5964e980bc 100644 --- a/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts +++ b/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts @@ -5,7 +5,7 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { Disposable } from 'vs/base/common/lifecycle'; -import { basename, joinPath } from 'vs/base/common/resources'; +import { joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; @@ -16,9 +16,12 @@ import { IFileService } from 'vs/platform/files/common/files'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; -import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { EXTENSIONS_RESOURCE_NAME, IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceContextService, IWorkspaceIdentifier, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IExtensionManagementServerService, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { CreationOptions, IUserDataProfileManagementService, IUserDataProfileService, IUserDataProfileTemplate, PROFILES_CATEGORY } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; @@ -45,11 +48,25 @@ export class UserDataProfileManagementService extends Disposable implements IUse @IDialogService private readonly dialogService: IDialogService, @IProgressService private readonly progressService: IProgressService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, + @IStorageService private readonly storageService: IStorageService, + @IExtensionService private readonly extensionService: IExtensionService, + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @ILogService logService: ILogService ) { super(); } + private async checkAndCreateExtensionsProfileResource(): Promise { + if (this.userDataProfileService.currentProfile.extensionsResource) { + return this.userDataProfileService.currentProfile.extensionsResource; + } + if (!this.userDataProfileService.defaultProfile.extensionsResource) { + // Extensions profile is not yet created for default profile, create it now + return this.createDefaultExtensionsProfile(joinPath(this.userDataProfilesService.defaultProfile.location, EXTENSIONS_RESOURCE_NAME)); + } + throw new Error('Invalid Profile'); + } + async createAndEnterProfile(name: string, options: CreationOptions = DefaultOptions, fromExisting?: boolean): Promise { const workspaceIdentifier = this.getWorkspaceIdentifier(); if (!workspaceIdentifier) { @@ -58,18 +75,17 @@ export class UserDataProfileManagementService extends Disposable implements IUse const promises: Promise[] = []; const newProfile = this.userDataProfilesService.newProfile(name); await this.fileService.createFolder(newProfile.location); + const extensionsProfileResourcePromise = this.checkAndCreateExtensionsProfileResource(); + promises.push(extensionsProfileResourcePromise); if (fromExisting) { if (options?.uiState) { - promises.push(this.fileService.copy(this.userDataProfileService.currentProfile.globalStorageHome, newProfile.globalStorageHome)); + // No op because, migration is handled by storage service while entering profile } if (options?.settings) { promises.push(this.fileService.copy(this.userDataProfileService.currentProfile.settingsResource, newProfile.settingsResource)); } - if (options?.extensions && newProfile.extensionsResource) { - promises.push((async () => { - const extensionsProfileResource = this.userDataProfileService.currentProfile.extensionsResource ?? await this.createDefaultExtensionsProfile(joinPath(this.userDataProfilesService.defaultProfile.location, basename(newProfile.extensionsResource!))); - this.fileService.copy(extensionsProfileResource, newProfile.extensionsResource!); - })()); + if (options?.extensions) { + promises.push((async () => this.fileService.copy(await extensionsProfileResourcePromise, newProfile.extensionsResource))()); } if (options?.keybindings) { promises.push(this.fileService.copy(this.userDataProfileService.currentProfile.keybindingsResource, newProfile.keybindingsResource)); @@ -82,13 +98,10 @@ export class UserDataProfileManagementService extends Disposable implements IUse } } else { promises.push(this.fileService.createFolder(newProfile.globalStorageHome)); - if (!this.userDataProfilesService.defaultProfile.extensionsResource) { - promises.push(this.createDefaultExtensionsProfile(joinPath(this.userDataProfilesService.defaultProfile.location, basename(newProfile.extensionsResource!)))); - } } await Promise.allSettled(promises); - await this.userDataProfilesService.createProfile(newProfile, options, workspaceIdentifier); - await this.enterProfile(); + const createdProfile = await this.userDataProfilesService.createProfile(newProfile, options, workspaceIdentifier); + await this.enterProfile(createdProfile, !!fromExisting); } async removeProfile(profile: IUserDataProfile): Promise { @@ -118,7 +131,7 @@ export class UserDataProfileManagementService extends Disposable implements IUse throw new Error(`Profile ${profile.name} does not exist`); } await this.userDataProfilesService.setProfileForWorkspace(profile, workspaceIdentifier); - await this.enterProfile(); + await this.enterProfile(profile, false); } async createAndEnterProfileFromTemplate(name: string, template: IUserDataProfileTemplate, options: CreationOptions = DefaultOptions): Promise { @@ -126,7 +139,7 @@ export class UserDataProfileManagementService extends Disposable implements IUse if (!workspaceIdentifier) { throw new Error(localize('cannotCreateProfileInEmptyWorkbench', "Cannot create a profile in an empty workspace")); } - await this.progressService.withProgress({ + const profile = await this.progressService.withProgress({ location: ProgressLocation.Notification, title: localize('profiles.creating', "{0}: Creating...", PROFILES_CATEGORY), }, async progress => { @@ -143,9 +156,9 @@ export class UserDataProfileManagementService extends Disposable implements IUse promises.push(this.fileService.writeFile(newProfile.extensionsResource, VSBuffer.fromString(template.extensions))); } await Promise.allSettled(promises); - await this.userDataProfilesService.createProfile(newProfile, options, workspaceIdentifier); + return this.userDataProfilesService.createProfile(newProfile, options, workspaceIdentifier); }); - await this.enterProfile(); + await this.enterProfile(profile, false); } private getWorkspaceIdentifier(): ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier | undefined { @@ -159,15 +172,23 @@ export class UserDataProfileManagementService extends Disposable implements IUse return undefined; } - private async enterProfile(): Promise { - const result = await this.dialogService.confirm({ - type: 'info', - message: localize('reload message', "Switching a profile requires reloading VS Code."), - primaryButton: localize('reload button', "&&Reload"), - }); - if (result.confirmed) { - await this.hostService.reload(); + private async enterProfile(profile: IUserDataProfile, preserveData: boolean): Promise { + if (this.environmentService.remoteAuthority) { + const result = await this.dialogService.confirm({ + type: 'info', + message: localize('reload message', "Switching a profile requires reloading VS Code."), + primaryButton: localize('reload button', "&&Reload"), + }); + if (result.confirmed) { + await this.hostService.reload(); + } + return; } + + this.extensionService.stopExtensionHosts(); + await this.storageService.switch(profile, preserveData); + await this.userDataProfileService.updateCurrentProfile(profile); + await this.extensionService.startExtensionHosts(); } private async createDefaultExtensionsProfile(extensionsProfileResource: URI): Promise { diff --git a/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts b/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts index a033e55d951..9a3fefc8b16 100644 --- a/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts +++ b/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts @@ -19,13 +19,18 @@ export type CreationOptions = { uiState?: boolean; }; +export interface DidChangeUserDataProfileEvent { + readonly profile: IUserDataProfile; + join(promise: Promise): void; +} + export const IUserDataProfileService = createDecorator('IUserDataProfileService'); export interface IUserDataProfileService { readonly _serviceBrand: undefined; readonly defaultProfile: IUserDataProfile; - readonly onDidChangeCurrentProfile: Event; + readonly onDidChangeCurrentProfile: Event; readonly currentProfile: IUserDataProfile; - updateCurrentProfile(currentProfile: IUserDataProfile): void; + updateCurrentProfile(currentProfile: IUserDataProfile): Promise; } export const IUserDataProfileManagementService = createDecorator('IUserDataProfileManagementService'); diff --git a/src/vs/workbench/services/userDataProfile/common/userDataProfileService.ts b/src/vs/workbench/services/userDataProfile/common/userDataProfileService.ts index 756d7acc6cd..8e94bbddafa 100644 --- a/src/vs/workbench/services/userDataProfile/common/userDataProfileService.ts +++ b/src/vs/workbench/services/userDataProfile/common/userDataProfileService.ts @@ -3,10 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Promises } from 'vs/base/common/async'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; -import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; +import { DidChangeUserDataProfileEvent, IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; export class UserDataProfileService extends Disposable implements IUserDataProfileService { @@ -14,7 +15,7 @@ export class UserDataProfileService extends Disposable implements IUserDataProfi readonly defaultProfile: IUserDataProfile; - private readonly _onDidChangeCurrentProfile = this._register(new Emitter()); + private readonly _onDidChangeCurrentProfile = this._register(new Emitter()); readonly onDidChangeCurrentProfile = this._onDidChangeCurrentProfile.event; private _currentProfile: IUserDataProfile; @@ -29,8 +30,15 @@ export class UserDataProfileService extends Disposable implements IUserDataProfi this._currentProfile = currentProfile; } - updateCurrentProfile(userDataProfile: IUserDataProfile): void { + async updateCurrentProfile(userDataProfile: IUserDataProfile): Promise { this._currentProfile = userDataProfile; - this._onDidChangeCurrentProfile.fire(userDataProfile); + const joiners: Promise[] = []; + this._onDidChangeCurrentProfile.fire({ + profile: userDataProfile, + join(promise) { + joiners.push(promise); + } + }); + await Promises.settled(joiners); } } diff --git a/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts b/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts index 5e13ba82ef2..e2e9b8029c4 100644 --- a/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts @@ -20,13 +20,13 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { Schemas } from 'vs/base/common/network'; import { SaveReason } from 'vs/workbench/common/editor'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; +import { IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; export abstract class AbstractWorkspaceEditingService implements IWorkspaceEditingService { @@ -35,7 +35,7 @@ export abstract class AbstractWorkspaceEditingService implements IWorkspaceEditi constructor( @IJSONEditingService private readonly jsonEditingService: IJSONEditingService, @IWorkspaceContextService protected readonly contextService: WorkspaceService, - @IConfigurationService private readonly configurationService: IConfigurationService, + @IWorkbenchConfigurationService private readonly configurationService: IWorkbenchConfigurationService, @INotificationService private readonly notificationService: INotificationService, @ICommandService private readonly commandService: ICommandService, @IFileService private readonly fileService: IFileService, @@ -359,8 +359,7 @@ export abstract class AbstractWorkspaceEditingService implements IWorkspaceEditi await this.migrateWorkspaceSettings(workspace); } - const workspaceImpl = this.contextService as WorkspaceService; - await workspaceImpl.initialize(workspace); + await this.configurationService.initialize(workspace); return this.workspacesService.enterWorkspace(workspaceUri); } diff --git a/src/vs/workbench/services/workspaces/browser/workspaceEditingService.ts b/src/vs/workbench/services/workspaces/browser/workspaceEditingService.ts index b5230e4d641..e62d4d461b5 100644 --- a/src/vs/workbench/services/workspaces/browser/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/browser/workspaceEditingService.ts @@ -12,7 +12,6 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IFileService } from 'vs/platform/files/common/files'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { AbstractWorkspaceEditingService } from 'vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService'; @@ -21,13 +20,14 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { URI } from 'vs/base/common/uri'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; +import { IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; export class BrowserWorkspaceEditingService extends AbstractWorkspaceEditingService { constructor( @IJSONEditingService jsonEditingService: IJSONEditingService, @IWorkspaceContextService contextService: WorkspaceService, - @IConfigurationService configurationService: IConfigurationService, + @IWorkbenchConfigurationService configurationService: IWorkbenchConfigurationService, @INotificationService notificationService: INotificationService, @ICommandService commandService: ICommandService, @IFileService fileService: IFileService, diff --git a/src/vs/workbench/services/workspaces/common/workspaceTrust.ts b/src/vs/workbench/services/workspaces/common/workspaceTrust.ts index e5039e9bc04..e464de637ee 100644 --- a/src/vs/workbench/services/workspaces/common/workspaceTrust.ts +++ b/src/vs/workbench/services/workspaces/common/workspaceTrust.ts @@ -244,7 +244,7 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork } private loadTrustInfo(): IWorkspaceTrustInfo { - const infoAsString = this.storageService.get(this.storageKey, StorageScope.GLOBAL); + const infoAsString = this.storageService.get(this.storageKey, StorageScope.APPLICATION); let result: IWorkspaceTrustInfo | undefined; try { @@ -270,7 +270,7 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork } private async saveTrustInfo(): Promise { - this.storageService.store(this.storageKey, JSON.stringify(this._trustStateInfo), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(this.storageKey, JSON.stringify(this._trustStateInfo), StorageScope.APPLICATION, StorageTarget.MACHINE); this._onDidChangeTrustedFolders.fire(); await this.updateWorkspaceTrust(); diff --git a/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts b/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts index 0cbfcf1e2b8..20b61617f1a 100644 --- a/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts @@ -20,7 +20,6 @@ import { IFileService } from 'vs/platform/files/common/files'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { ILifecycleService, ShutdownReason } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILabelService } from 'vs/platform/label/common/label'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; @@ -32,6 +31,7 @@ import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { WorkingCopyBackupService } from 'vs/workbench/services/workingCopy/common/workingCopyBackupService'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; +import { IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; export class NativeWorkspaceEditingService extends AbstractWorkspaceEditingService { @@ -39,7 +39,7 @@ export class NativeWorkspaceEditingService extends AbstractWorkspaceEditingServi @IJSONEditingService jsonEditingService: IJSONEditingService, @IWorkspaceContextService contextService: WorkspaceService, @INativeHostService private nativeHostService: INativeHostService, - @IConfigurationService configurationService: IConfigurationService, + @IWorkbenchConfigurationService configurationService: IWorkbenchConfigurationService, @IStorageService private storageService: IStorageService, @IExtensionService private extensionService: IExtensionService, @IWorkingCopyBackupService private workingCopyBackupService: IWorkingCopyBackupService, diff --git a/src/vs/workbench/services/workspaces/test/common/workspaceTrust.test.ts b/src/vs/workbench/services/workspaces/test/common/workspaceTrust.test.ts index cb8808efc50..ab6f9d78a7d 100644 --- a/src/vs/workbench/services/workspaces/test/common/workspaceTrust.test.ts +++ b/src/vs/workbench/services/workspaces/test/common/workspaceTrust.test.ts @@ -109,7 +109,7 @@ suite('Workspace Trust', () => { test('empty workspace - trusted, open trusted file', async () => { await configurationService.setUserConfiguration('security', getUserSettings(true, true)); const trustInfo: IWorkspaceTrustInfo = { uriTrustInfo: [{ uri: URI.parse('file:///Folder'), trusted: true }] }; - storageService.store(WORKSPACE_TRUST_STORAGE_KEY, JSON.stringify(trustInfo), StorageScope.GLOBAL, StorageTarget.MACHINE); + storageService.store(WORKSPACE_TRUST_STORAGE_KEY, JSON.stringify(trustInfo), StorageScope.APPLICATION, StorageTarget.MACHINE); (environmentService as any).filesToOpenOrCreate = [{ fileUri: URI.parse('file:///Folder/file.txt') }]; instantiationService.stub(IWorkbenchEnvironmentService, { ...environmentService }); From 0bc31097613ab4e16c1e4dda85750bc0326e77e6 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 20 Jun 2022 10:44:50 -0700 Subject: [PATCH 172/175] Don't try parsing non-markdown files (#152661) This fixes our references and rename provider to not try parsing non-markdown files as if they were markdown --- .../src/languageFeatures/diagnostics.ts | 18 ++++++----- .../src/languageFeatures/references.ts | 30 +++++++++++-------- .../src/languageFeatures/rename.ts | 5 ++-- .../src/languageFeatures/workspaceCache.ts | 2 +- .../src/test/inMemoryWorkspace.ts | 6 +++- .../src/util/file.ts | 19 +++++++++++- .../src/util/schemes.ts | 12 -------- .../src/workspaceContents.ts | 25 ++++++++++------ 8 files changed, 71 insertions(+), 46 deletions(-) diff --git a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts index cea19b2d435..4a0a30a79a6 100644 --- a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts @@ -13,12 +13,12 @@ import { MdTableOfContentsWatcher } from '../test/tableOfContentsWatcher'; import { Delayer } from '../util/async'; import { noopToken } from '../util/cancellation'; import { Disposable } from '../util/dispose'; -import { isMarkdownFile } from '../util/file'; +import { isMarkdownFile, looksLikeMarkdownPath } from '../util/file'; import { Limiter } from '../util/limiter'; import { ResourceMap } from '../util/resourceMap'; import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; import { InternalHref, LinkDefinitionSet, MdLink, MdLinkProvider, MdLinkSource } from './documentLinkProvider'; -import { MdReferencesProvider, tryFindMdDocumentForLink } from './references'; +import { MdReferencesProvider, tryResolveLinkPath } from './references'; const localize = nls.loadMessageBundle(); @@ -374,7 +374,7 @@ export class DiagnosticManager extends Disposable { this.pendingDiagnostics.clear(); await Promise.all(pending.map(async resource => { - const doc = await this.workspaceContents.getMarkdownDocument(resource); + const doc = await this.workspaceContents.getOrLoadMarkdownDocument(resource); if (doc) { await this.inFlightDiagnostics.trigger(doc.uri, async (token) => { const state = await this.recomputeDiagnosticState(doc, token); @@ -540,19 +540,19 @@ export class DiagnosticComputer { return; } - const hrefDoc = await tryFindMdDocumentForLink({ kind: 'internal', path: path, fragment: '' }, this.workspaceContents); - if (!hrefDoc && !await this.workspaceContents.pathExists(path)) { + const resolvedHrefPath = await tryResolveLinkPath(path, this.workspaceContents); + if (!resolvedHrefPath) { const msg = localize('invalidPathLink', 'File does not exist at path: {0}', path.fsPath); for (const link of links) { if (!this.isIgnoredLink(options, link.source.pathText)) { diagnostics.push(new LinkDoesNotExistDiagnostic(link.source.hrefRange, msg, pathErrorSeverity, link.source.pathText)); } } - } else if (hrefDoc && typeof fragmentErrorSeverity !== 'undefined') { + } else if (typeof fragmentErrorSeverity !== 'undefined' && this.isMarkdownPath(resolvedHrefPath)) { // Validate each of the links to headers in the file const fragmentLinks = links.filter(x => x.fragment); if (fragmentLinks.length) { - const toc = await this.tocProvider.get(hrefDoc.uri); + const toc = await this.tocProvider.get(resolvedHrefPath); for (const link of fragmentLinks) { if (!toc.lookup(link.fragment) && !this.isIgnoredLink(options, link.source.pathText) && !this.isIgnoredLink(options, link.source.text)) { const msg = localize('invalidLinkToHeaderInOtherFile', 'Header does not exist in file: {0}', link.fragment); @@ -567,6 +567,10 @@ export class DiagnosticComputer { return diagnostics; } + private isMarkdownPath(resolvedHrefPath: vscode.Uri) { + return this.workspaceContents.hasMarkdownDocument(resolvedHrefPath) || looksLikeMarkdownPath(resolvedHrefPath); + } + private isIgnoredLink(options: DiagnosticOptions, link: string): boolean { return options.ignoreLinks.some(glob => picomatch.isMatch(link, glob)); } diff --git a/extensions/markdown-language-features/src/languageFeatures/references.ts b/extensions/markdown-language-features/src/languageFeatures/references.ts index 7e99b34e569..f532a36bb2f 100644 --- a/extensions/markdown-language-features/src/languageFeatures/references.ts +++ b/extensions/markdown-language-features/src/languageFeatures/references.ts @@ -8,6 +8,7 @@ import { MarkdownEngine } from '../markdownEngine'; import { MdTableOfContentsProvider, TocEntry } from '../tableOfContents'; import { noopToken } from '../util/cancellation'; import { Disposable } from '../util/dispose'; +import { looksLikeMarkdownPath } from '../util/file'; import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; import { InternalHref, MdLink, MdLinkComputer } from './documentLinkProvider'; import { MdWorkspaceInfoCache } from './workspaceCache'; @@ -177,15 +178,15 @@ export class MdReferencesProvider extends Disposable { return references; } - const targetDoc = await tryFindMdDocumentForLink(sourceLink.href, this.workspaceContents); + const resolvedResource = await tryResolveLinkPath(sourceLink.href.path, this.workspaceContents); if (token.isCancellationRequested) { return []; } const references: MdReference[] = []; - if (targetDoc && sourceLink.href.fragment && sourceLink.source.fragmentRange?.contains(triggerPosition)) { - const toc = await this.tocProvider.get(targetDoc.uri); + if (resolvedResource && this.isMarkdownPath(resolvedResource) && sourceLink.href.fragment && sourceLink.source.fragmentRange?.contains(triggerPosition)) { + const toc = await this.tocProvider.get(resolvedResource); const entry = toc.lookup(sourceLink.href.fragment); if (entry) { references.push({ @@ -199,7 +200,7 @@ export class MdReferencesProvider extends Disposable { } for (const link of allLinksInWorkspace) { - if (link.href.kind !== 'internal' || !this.looksLikeLinkToDoc(link.href, targetDoc.uri)) { + if (link.href.kind !== 'internal' || !this.looksLikeLinkToDoc(link.href, resolvedResource)) { continue; } @@ -215,12 +216,16 @@ export class MdReferencesProvider extends Disposable { } } } else { // Triggered on a link without a fragment so we only require matching the file and ignore fragments - references.push(...this.findAllLinksToFile(targetDoc?.uri ?? sourceLink.href.path, allLinksInWorkspace, sourceLink)); + references.push(...this.findAllLinksToFile(resolvedResource ?? sourceLink.href.path, allLinksInWorkspace, sourceLink)); } return references; } + private isMarkdownPath(resolvedHrefPath: vscode.Uri) { + return this.workspaceContents.hasMarkdownDocument(resolvedHrefPath) || looksLikeMarkdownPath(resolvedHrefPath); + } + private looksLikeLinkToDoc(href: InternalHref, targetDoc: vscode.Uri) { return href.path.fsPath === targetDoc.fsPath || uri.Utils.extname(href.path) === '' && href.path.with({ path: href.path.path + '.md' }).fsPath === targetDoc.fsPath; @@ -310,16 +315,17 @@ export function registerReferencesSupport( return vscode.languages.registerReferenceProvider(selector, new MdVsCodeReferencesProvider(referencesProvider)); } -export async function tryFindMdDocumentForLink(href: InternalHref, workspaceContents: MdWorkspaceContents): Promise { - const targetDoc = await workspaceContents.getMarkdownDocument(href.path); - if (targetDoc) { - return targetDoc; +export async function tryResolveLinkPath(originalUri: vscode.Uri, workspaceContents: MdWorkspaceContents): Promise { + if (await workspaceContents.pathExists(originalUri)) { + return originalUri; } // We don't think the file exists. If it doesn't already have an extension, try tacking on a `.md` and using that instead - if (uri.Utils.extname(href.path) === '') { - const dotMdResource = href.path.with({ path: href.path.path + '.md' }); - return workspaceContents.getMarkdownDocument(dotMdResource); + if (uri.Utils.extname(originalUri) === '') { + const dotMdResource = originalUri.with({ path: originalUri.path + '.md' }); + if (await workspaceContents.pathExists(dotMdResource)) { + return dotMdResource; + } } return undefined; diff --git a/extensions/markdown-language-features/src/languageFeatures/rename.ts b/extensions/markdown-language-features/src/languageFeatures/rename.ts index ef7527a26b2..a88e3ff4689 100644 --- a/extensions/markdown-language-features/src/languageFeatures/rename.ts +++ b/extensions/markdown-language-features/src/languageFeatures/rename.ts @@ -11,7 +11,7 @@ import { Disposable } from '../util/dispose'; import { resolveDocumentLink } from '../util/openDocumentLink'; import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; import { InternalHref } from './documentLinkProvider'; -import { MdHeaderReference, MdLinkReference, MdReference, MdReferencesProvider, tryFindMdDocumentForLink } from './references'; +import { MdHeaderReference, MdLinkReference, MdReference, MdReferencesProvider, tryResolveLinkPath } from './references'; const localize = nls.loadMessageBundle(); @@ -153,8 +153,7 @@ export class MdVsCodeRenameProvider extends Disposable implements vscode.RenameP const edit = new vscode.WorkspaceEdit(); const fileRenames: MdFileRenameEdit[] = []; - const targetDoc = await tryFindMdDocumentForLink(triggerHref, this.workspaceContents); - const targetUri = targetDoc?.uri ?? triggerHref.path; + const targetUri = await tryResolveLinkPath(triggerHref.path, this.workspaceContents) ?? triggerHref.path; const rawNewFilePath = resolveDocumentLink(newName, triggerDocument); let resolvedNewFilePath = rawNewFilePath; diff --git a/extensions/markdown-language-features/src/languageFeatures/workspaceCache.ts b/extensions/markdown-language-features/src/languageFeatures/workspaceCache.ts index 50ef9ebef52..8d7e3c81fa2 100644 --- a/extensions/markdown-language-features/src/languageFeatures/workspaceCache.ts +++ b/extensions/markdown-language-features/src/languageFeatures/workspaceCache.ts @@ -61,7 +61,7 @@ export class MdDocumentInfoCache extends Disposable { return existing; } - const doc = await this.workspaceContents.getMarkdownDocument(resource); + const doc = await this.workspaceContents.getOrLoadMarkdownDocument(resource); return doc && this.onDidChangeDocument(doc, true)?.value; } diff --git a/extensions/markdown-language-features/src/test/inMemoryWorkspace.ts b/extensions/markdown-language-features/src/test/inMemoryWorkspace.ts index 53dab1a3e6d..2c53e0cde5e 100644 --- a/extensions/markdown-language-features/src/test/inMemoryWorkspace.ts +++ b/extensions/markdown-language-features/src/test/inMemoryWorkspace.ts @@ -22,10 +22,14 @@ export class InMemoryWorkspaceMarkdownDocuments implements MdWorkspaceContents { return Array.from(this._documents.values()); } - public async getMarkdownDocument(resource: vscode.Uri): Promise { + public async getOrLoadMarkdownDocument(resource: vscode.Uri): Promise { return this._documents.get(resource); } + public hasMarkdownDocument(resolvedHrefPath: vscode.Uri): boolean { + return this._documents.has(resolvedHrefPath); + } + public async pathExists(resource: vscode.Uri): Promise { return this._documents.has(resource); } diff --git a/extensions/markdown-language-features/src/util/file.ts b/extensions/markdown-language-features/src/util/file.ts index d61029aaf99..6d5f22e95e0 100644 --- a/extensions/markdown-language-features/src/util/file.ts +++ b/extensions/markdown-language-features/src/util/file.ts @@ -4,7 +4,24 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import * as URI from 'vscode-uri'; + +const markdownFileExtensions = Object.freeze([ + '.md', + '.mkd', + '.mdwn', + '.mdown', + '.markdown', + '.markdn', + '.mdtxt', + '.mdtext', + '.workbook', +]); export function isMarkdownFile(document: vscode.TextDocument) { return document.languageId === 'markdown'; -} \ No newline at end of file +} + +export function looksLikeMarkdownPath(resolvedHrefPath: vscode.Uri) { + return markdownFileExtensions.includes(URI.Utils.extname(resolvedHrefPath).toLowerCase()); +} diff --git a/extensions/markdown-language-features/src/util/schemes.ts b/extensions/markdown-language-features/src/util/schemes.ts index 02e5aba6590..7e87ef539a2 100644 --- a/extensions/markdown-language-features/src/util/schemes.ts +++ b/extensions/markdown-language-features/src/util/schemes.ts @@ -32,15 +32,3 @@ export function getUriForLinkWithKnownExternalScheme(link: string): vscode.Uri | export function isOfScheme(scheme: string, link: string): boolean { return link.toLowerCase().startsWith(scheme); } - -export const MarkdownFileExtensions: readonly string[] = [ - '.md', - '.mkd', - '.mdwn', - '.mdown', - '.markdown', - '.markdn', - '.mdtxt', - '.mdtext', - '.workbook', -]; diff --git a/extensions/markdown-language-features/src/workspaceContents.ts b/extensions/markdown-language-features/src/workspaceContents.ts index 6608f2b756c..a5e771f2d06 100644 --- a/extensions/markdown-language-features/src/workspaceContents.ts +++ b/extensions/markdown-language-features/src/workspaceContents.ts @@ -4,10 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import * as URI from 'vscode-uri'; import { coalesce } from './util/arrays'; import { Disposable } from './util/dispose'; -import { isMarkdownFile } from './util/file'; +import { isMarkdownFile, looksLikeMarkdownPath } from './util/file'; import { InMemoryDocument } from './util/inMemoryDocument'; import { Limiter } from './util/limiter'; import { ResourceMap } from './util/resourceMap'; @@ -42,7 +41,12 @@ export interface MdWorkspaceContents { */ getAllMarkdownDocuments(): Promise>; - getMarkdownDocument(resource: vscode.Uri): Promise; + /** + * Check if a document already exists in the workspace contents. + */ + hasMarkdownDocument(resource: vscode.Uri): boolean; + + getOrLoadMarkdownDocument(resource: vscode.Uri): Promise; pathExists(resource: vscode.Uri): Promise; @@ -84,7 +88,7 @@ export class VsCodeMdWorkspaceContents extends Disposable implements MdWorkspace const resources = await vscode.workspace.findFiles('**/*.md', '**/node_modules/**'); const onDiskResults = await Promise.all(resources.map(resource => { return limiter.queue(async () => { - const doc = await this.getMarkdownDocument(resource); + const doc = await this.getOrLoadMarkdownDocument(resource); if (doc) { foundFiles.add(doc.uri.toString()); } @@ -123,14 +127,14 @@ export class VsCodeMdWorkspaceContents extends Disposable implements MdWorkspace this._register(this._watcher.onDidChange(async resource => { this._documentCache.delete(resource); - const document = await this.getMarkdownDocument(resource); + const document = await this.getOrLoadMarkdownDocument(resource); if (document) { this._onDidChangeMarkdownDocumentEmitter.fire(document); } })); this._register(this._watcher.onDidCreate(async resource => { - const document = await this.getMarkdownDocument(resource); + const document = await this.getOrLoadMarkdownDocument(resource); if (document) { this._onDidCreateMarkdownDocumentEmitter.fire(document); } @@ -160,7 +164,7 @@ export class VsCodeMdWorkspaceContents extends Disposable implements MdWorkspace return isMarkdownFile(doc) && doc.uri.scheme !== 'vscode-bulkeditpreview'; } - public async getMarkdownDocument(resource: vscode.Uri): Promise { + public async getOrLoadMarkdownDocument(resource: vscode.Uri): Promise { const existing = this._documentCache.get(resource); if (existing) { return existing; @@ -172,8 +176,7 @@ export class VsCodeMdWorkspaceContents extends Disposable implements MdWorkspace return matchingDocument; } - const ext = URI.Utils.extname(resource).toLowerCase(); - if (ext !== '.md') { + if (!looksLikeMarkdownPath(resource)) { return undefined; } @@ -190,6 +193,10 @@ export class VsCodeMdWorkspaceContents extends Disposable implements MdWorkspace } } + public hasMarkdownDocument(resolvedHrefPath: vscode.Uri): boolean { + return this._documentCache.has(resolvedHrefPath); + } + public async pathExists(target: vscode.Uri): Promise { let targetResourceStat: vscode.FileStat | undefined; try { From 3bb4ee09d538452a772e5af1fcbe70d5a5163aee Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Mon, 20 Jun 2022 14:05:51 -0400 Subject: [PATCH 173/175] Fix #152543 (#152651) --- .../workbench/contrib/files/browser/views/explorerViewer.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 4064696d2bd..c42d94b385d 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -760,11 +760,9 @@ export class FilesFilter implements ITreeFilter { filter(stat: ExplorerItem, parentVisibility: TreeVisibility): boolean { // Add newly visited .gitignore files to the ignore tree - if (stat.name === '.gitignore') { + if (stat.name === '.gitignore' && this.ignoreTreesPerRoot.has(stat.resource.toString())) { this.processIgnoreFile(stat.root.resource.toString(), stat.resource, false); - // Never hide .gitignore files if explorer.excludeGitIgnore setting is enabled - // We can tell it's enabled if there is an ignore tree for the workspace root - return !!this.ignoreTreesPerRoot.get(stat.root.resource.toString()); + return true; } return this.isVisible(stat, parentVisibility); From 273aa337e2abbebd5c606da01d77b5165fb86cec Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Mon, 20 Jun 2022 14:06:31 -0400 Subject: [PATCH 174/175] Preserve properties and measurements (#152665) Don't spread data --- src/vs/platform/telemetry/browser/1dsAppender.ts | 2 +- src/vs/platform/telemetry/node/1dsAppender.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/telemetry/browser/1dsAppender.ts b/src/vs/platform/telemetry/browser/1dsAppender.ts index 74dfaa90175..ceee3f6414f 100644 --- a/src/vs/platform/telemetry/browser/1dsAppender.ts +++ b/src/vs/platform/telemetry/browser/1dsAppender.ts @@ -106,7 +106,7 @@ export class OneDataSystemWebAppender implements ITelemetryAppender { try { this._withAIClient((aiClient) => aiClient.track({ name: this._eventPrefix + '/' + eventName, - data: { ...data.properties, ...data.measurements }, + data, })); } catch { } diff --git a/src/vs/platform/telemetry/node/1dsAppender.ts b/src/vs/platform/telemetry/node/1dsAppender.ts index 8f604ef9cc1..35ca15f81b4 100644 --- a/src/vs/platform/telemetry/node/1dsAppender.ts +++ b/src/vs/platform/telemetry/node/1dsAppender.ts @@ -135,7 +135,7 @@ export class OneDataSystemAppender implements ITelemetryAppender { try { this._withAIClient((aiClient) => aiClient.track({ name: this._eventPrefix + '/' + eventName, - data: { ...data.properties, ...data.measurements }, + data, })); } catch { } From 2d4988b53b9d7d8c309ffe9f7ef457b4b8fd672e Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Mon, 20 Jun 2022 12:07:38 -0700 Subject: [PATCH 175/175] Prefer third party renderer over builtin one. (#152670) --- .../workbench/contrib/notebook/browser/notebookServiceImpl.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 374f1d707a1..055e95e8a91 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -317,8 +317,8 @@ export class NotebookOutputRendererInfoStore { const enum ReuseOrder { PreviouslySelected = 1 << 8, SameExtensionAsNotebook = 2 << 8, - BuiltIn = 3 << 8, - OtherRenderer = 4 << 8, + OtherRenderer = 3 << 8, + BuiltIn = 4 << 8, } const preferred = notebookProviderInfo && this.preferredMimetype.getValue()[notebookProviderInfo.id]?.[mimeType];