From 9405808b23f254e1ce619a6cdc75635a94cb8d45 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 6 Apr 2022 10:39:47 -0700 Subject: [PATCH 001/245] Use SELECT_KERNEL_ID other than string literal --- .../browser/contrib/editorStatusBar/editorStatusBar.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts index cb7ab0f8586..83b35ce916d 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts @@ -38,7 +38,7 @@ import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeat registerAction2(class extends Action2 { constructor() { super({ - id: '_notebook.selectKernel', + id: SELECT_KERNEL_ID, category: NOTEBOOK_ACTIONS_CATEGORY, title: { value: nls.localize('notebookActions.selectKernel', "Select Notebook Kernel"), original: 'Select Notebook Kernel' }, // precondition: NOTEBOOK_IS_ACTIVE_EDITOR, @@ -381,7 +381,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution { tooltip: isSuggested ? nls.localize('tooltop', "{0} (suggestion)", tooltip) : tooltip, command: SELECT_KERNEL_ID, }, - '_notebook.selectKernel', + SELECT_KERNEL_ID, StatusbarAlignment.RIGHT, 10 )); @@ -399,7 +399,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution { command: SELECT_KERNEL_ID, backgroundColor: { id: 'statusBarItem.prominentBackground' } }, - '_notebook.selectKernel', + SELECT_KERNEL_ID, StatusbarAlignment.RIGHT, 10 )); From 78a20cf4dbc52699aa1b55a0fd7920e59dfb041b Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 6 Apr 2022 16:20:41 -0400 Subject: [PATCH 002/245] update strings and editor --- .../browser/untitledTextEditorHint.ts | 23 +++++++++++++++++++ .../files/browser/fileActions.contribution.ts | 6 ++--- .../contrib/files/browser/fileActions.ts | 2 +- .../files/browser/views/explorerView.ts | 2 +- .../common/gettingStartedContent.ts | 4 ++-- .../common/newFile.contribution.ts | 2 +- .../actions/common/menusExtensionPoint.ts | 2 +- 7 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint.ts b/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint.ts index 306f9b93ce7..4f2918ddd94 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint.ts @@ -112,6 +112,21 @@ class UntitledTextEditorHintContentWidget implements IContentWidget { language.title = localize('keyboardBindingTooltip', "{0}", languageKeybindingLabel); } this.domNode.appendChild(language); + + const or = $('span'); + or.innerText = localize('or', " or ",); + this.domNode.appendChild(or); + + const chooseEditor = $('a.choose-editor'); + chooseEditor.style.cursor = 'pointer'; + chooseEditor.innerText = localize('chooseEditor', "select a different editor"); + const chooseEditorKeyBinding = this.keybindingService.lookupKeybinding('welcome.showNewFileEntries'); + const chooseEditorKeybindingLabel = chooseEditorKeyBinding?.getLabel(); + if (chooseEditorKeybindingLabel) { + chooseEditor.title = localize('chooseEditorBindingTooltip', "{0}", chooseEditorKeybindingLabel); + } + this.domNode.appendChild(chooseEditor); + const toGetStarted = $('span'); toGetStarted.innerText = localize('toGetStarted', " to get started. Start typing to dismiss, or ",); this.domNode.appendChild(toGetStarted); @@ -136,6 +151,14 @@ class UntitledTextEditorHintContentWidget implements IContentWidget { this.toDispose.push(dom.addDisposableListener(language, GestureEventType.Tap, languageOnClickOrTap)); this.toDispose.push(Gesture.addTarget(language)); + const chooseEditorOnClickOrTap = async (e: MouseEvent) => { + e.stopPropagation(); + await this.commandService.executeCommand('welcome.showNewFileEntries', { from: 'hint' }); + }; + this.toDispose.push(dom.addDisposableListener(chooseEditor, 'click', chooseEditorOnClickOrTap)); + this.toDispose.push(dom.addDisposableListener(chooseEditor, GestureEventType.Tap, chooseEditorOnClickOrTap)); + this.toDispose.push(Gesture.addTarget(chooseEditor)); + const dontShowOnClickOrTap = () => { this.configurationService.updateValue(untitledTextEditorHintSetting, 'hidden'); this.dispose(); diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index d88bd1c2d2b..3825aad9830 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -206,7 +206,7 @@ appendToCommandPalette(SAVE_FILES_COMMAND_ID, { value: nls.localize('saveFiles', appendToCommandPalette(REVERT_FILE_COMMAND_ID, { value: nls.localize('revert', "Revert File"), original: 'Revert File' }, category); appendToCommandPalette(COMPARE_WITH_SAVED_COMMAND_ID, { value: nls.localize('compareActiveWithSaved', "Compare Active File with Saved"), original: 'Compare Active File with Saved' }, category); appendToCommandPalette(SAVE_FILE_AS_COMMAND_ID, { value: SAVE_FILE_AS_LABEL, original: 'Save As...' }, category); -appendToCommandPalette(NEW_FILE_COMMAND_ID, { value: NEW_FILE_LABEL, original: 'New File' }, category, WorkspaceFolderCountContext.notEqualsTo('0')); +appendToCommandPalette(NEW_FILE_COMMAND_ID, { value: NEW_FILE_LABEL, original: 'New Text File' }, category, WorkspaceFolderCountContext.notEqualsTo('0')); appendToCommandPalette(NEW_FOLDER_COMMAND_ID, { value: NEW_FOLDER_LABEL, original: 'New Folder' }, category, WorkspaceFolderCountContext.notEqualsTo('0')); appendToCommandPalette(NEW_UNTITLED_FILE_COMMAND_ID, { value: NEW_UNTITLED_FILE_LABEL, original: 'New Untitled File' }, category); @@ -576,7 +576,7 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { }); // Empty Editor Group Context Menu -MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: NEW_UNTITLED_FILE_COMMAND_ID, title: nls.localize('newFile', "New File") }, group: '1_file', order: 10 }); +MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: NEW_UNTITLED_FILE_COMMAND_ID, title: nls.localize('newFile', "New Text File") }, group: '1_file', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EmptyEditorGroupContext, { command: { id: 'workbench.action.quickOpen', title: nls.localize('openFile', "Open File...") }, group: '1_file', order: 20 }); // File menu @@ -585,7 +585,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { group: '1_new', command: { id: NEW_UNTITLED_FILE_COMMAND_ID, - title: nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New File") + title: nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New Text File") }, order: 1 }); diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index d3100d24946..e482338009e 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -53,7 +53,7 @@ import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteA import { IPathService } from 'vs/workbench/services/path/common/pathService'; export const NEW_FILE_COMMAND_ID = 'explorer.newFile'; -export const NEW_FILE_LABEL = nls.localize('newFile', "New File"); +export const NEW_FILE_LABEL = nls.localize('newFile', "New Text File"); export const NEW_FOLDER_COMMAND_ID = 'explorer.newFolder'; export const NEW_FOLDER_LABEL = nls.localize('newFolder', "New Folder"); export const TRIGGER_RENAME_LABEL = nls.localize('rename', "Rename"); diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index c82cc35150a..a7885058f6a 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -891,7 +891,7 @@ registerAction2(class extends Action2 { constructor() { super({ id: 'workbench.files.action.createFileFromExplorer', - title: nls.localize('createNewFile', "New File"), + title: nls.localize('createNewEditor', "New Editor"), f1: false, icon: Codicon.newFile, precondition: ExplorerResourceNotReadonlyContext, diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts index a091be87b19..ac4305b3e3d 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts @@ -57,8 +57,8 @@ type GettingStartedStartEntryContent = BuiltinGettingStartedStartEntry[]; export const startEntries: GettingStartedStartEntryContent = [ { id: 'welcome.showNewFileEntries', - title: localize('gettingStarted.newFile.title', "New File..."), - description: localize('gettingStarted.newFile.description', "Open a new untitled file, notebook, or custom editor."), + title: localize('gettingStarted.newEditor.title', "New Editor..."), + description: localize('gettingStarted.newEditor.description', "Open a new untitled file, notebook, or custom editor."), icon: Codicon.newFile, content: { type: 'startEntry', diff --git a/src/vs/workbench/contrib/welcomeViews/common/newFile.contribution.ts b/src/vs/workbench/contrib/welcomeViews/common/newFile.contribution.ts index 7e54169a228..1fe5734dd99 100644 --- a/src/vs/workbench/contrib/welcomeViews/common/newFile.contribution.ts +++ b/src/vs/workbench/contrib/welcomeViews/common/newFile.contribution.ts @@ -25,7 +25,7 @@ registerAction2(class extends Action2 { constructor() { super({ id: 'welcome.showNewFileEntries', - title: { value: localize('welcome.newFile', "New File..."), original: 'New File...' }, + title: { value: localize('welcome.newEditor', "New Editor..."), original: 'New Editor...' }, category, f1: true, keybinding: { diff --git a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts index 7e06c120f95..3aa9a036090 100644 --- a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts +++ b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts @@ -242,7 +242,7 @@ const apiMenus: IAPIMenu[] = [ { key: 'file/newFile', id: MenuId.NewFile, - description: localize('file.newFile', "The 'New File...' quick pick, shown on welcome page and File menu."), + description: localize('file.newEditor', "The 'New Editor...' quick pick, shown on welcome page and File menu."), supportsSubmenus: false, }, { From 860221a1933842f41afc19c304ed3c1a04da0671 Mon Sep 17 00:00:00 2001 From: rebornix Date: Mon, 11 Apr 2022 08:05:40 -0700 Subject: [PATCH 003/245] two kernel types --- .../api/browser/mainThreadNotebookKernels.ts | 94 ++++++++++++- .../workbench/api/common/extHost.api.impl.ts | 4 + .../workbench/api/common/extHost.protocol.ts | 14 ++ .../api/common/extHostNotebookKernels.ts | 131 +++++++++++++++++- .../editorStatusBar/editorStatusBar.ts | 5 + .../notebook/browser/controller/apiActions.ts | 6 +- .../notebook/browser/notebookEditorWidget.ts | 10 +- .../browser/notebookExecutionServiceImpl.ts | 24 +++- .../browser/notebookKernelServiceImpl.ts | 28 +++- .../browser/view/cellParts/cellExecution.ts | 3 +- .../browser/view/cellParts/cellOutput.ts | 9 +- .../view/renderers/backLayerWebView.ts | 12 +- .../notebookEditorWidgetContextKeys.ts | 4 +- .../viewParts/notebookKernelActionViewItem.ts | 27 +++- .../notebook/common/notebookKernelService.ts | 37 ++++- .../browser/notebookExecutionService.test.ts | 5 +- .../notebookExecutionStateService.test.ts | 5 +- .../browser/notebookKernelService.test.ts | 5 +- .../common/extensionsApiProposals.ts | 1 + ...code.proposed.notebookProxyController.d.ts | 56 ++++++++ 20 files changed, 445 insertions(+), 35 deletions(-) create mode 100644 src/vscode-dts/vscode.proposed.notebookProxyController.d.ts diff --git a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts index 46febd36991..b94625b1bb6 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts @@ -15,11 +15,12 @@ import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/ext import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/notebookEditorService'; import { INotebookCellExecution, INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; -import { INotebookKernel, INotebookKernelChangeEvent, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { IResolvedNotebookKernel, INotebookKernelChangeEvent, INotebookKernelService, INotebookProxyKernel, INotebookProxyKernelChangeEvent, ProxyKernelState, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { SerializableObjectWithBuffers } from 'vs/workbench/services/extensions/common/proxyIdentifier'; -import { ExtHostContext, ExtHostNotebookKernelsShape, ICellExecuteUpdateDto, ICellExecutionCompleteDto, INotebookKernelDto2, MainContext, MainThreadNotebookKernelsShape } from '../common/extHost.protocol'; +import { ExtHostContext, ExtHostNotebookKernelsShape, ICellExecuteUpdateDto, ICellExecutionCompleteDto, INotebookKernelDto2, INotebookProxyKernelDto, MainContext, MainThreadNotebookKernelsShape } from '../common/extHost.protocol'; -abstract class MainThreadKernel implements INotebookKernel { +abstract class MainThreadKernel implements IResolvedNotebookKernel { + readonly type: NotebookKernelType.Resolved = NotebookKernelType.Resolved; private readonly _onDidChange = new Emitter(); private readonly preloads: { uri: URI; provides: string[] }[]; @@ -97,6 +98,58 @@ abstract class MainThreadKernel implements INotebookKernel { abstract cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise; } +abstract class MainThreadProxyKernel implements INotebookProxyKernel { + readonly type: NotebookKernelType.Proxy = NotebookKernelType.Proxy; + protected readonly _onDidChange = new Emitter(); + readonly onDidChange: Event = this._onDidChange.event; + readonly id: string; + readonly viewType: string; + readonly extension: ExtensionIdentifier; + label: string; + description?: string; + detail?: string; + kind?: string; + supportedLanguages: string[] = []; + connectionState: ProxyKernelState; + + constructor(data: INotebookProxyKernelDto) { + this.id = data.id; + this.viewType = data.notebookType; + this.extension = data.extensionId; + + this.label = data.label; + this.description = data.description; + this.detail = data.detail; + this.kind = data.kind; + + this.connectionState = ProxyKernelState.Disconnected; + } + + update(data: Partial) { + const event: INotebookProxyKernelChangeEvent = Object.create(null); + if (data.label !== undefined) { + this.label = data.label; + event.label = true; + } + if (data.description !== undefined) { + this.description = data.description; + event.description = true; + } + if (data.detail !== undefined) { + this.detail = data.detail; + event.detail = true; + } + if (data.kind !== undefined) { + this.kind = data.kind; + event.kind = true; + } + + this._onDidChange.fire(event); + } + + abstract resolveKernel(): Promise; +} + @extHostNamedCustomer(MainContext.MainThreadNotebookKernels) export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape { @@ -104,6 +157,7 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape private readonly _disposables = new DisposableStore(); private readonly _kernels = new Map(); + private readonly _proxyKernels = new Map(); private readonly _proxy: ExtHostNotebookKernelsShape; private readonly _executions = new Map(); @@ -243,6 +297,40 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape } } + // -- Proxy kernel + + async $addProxyKernel(handle: number, data: INotebookProxyKernelDto): Promise { + const that = this; + const proxyKernel = new class extends MainThreadProxyKernel { + async resolveKernel(): Promise { + this.connectionState = ProxyKernelState.Initializing; + this._onDidChange.fire({ connectionState: true }); + const delegateKernel = await that._proxy.$resolveKernel(handle); + this.connectionState = ProxyKernelState.Connected; + this._onDidChange.fire({ connectionState: true }); + return delegateKernel; + } + }(data); + + const listener = this._notebookKernelService.onDidChangeSelectedNotebooks(e => { + if (e.oldKernel === proxyKernel.id) { + this._proxy.$acceptNotebookAssociation(handle, e.notebook, false); + } else if (e.newKernel === proxyKernel.id) { + this._proxy.$acceptNotebookAssociation(handle, e.notebook, true); + } + }); + + const registration = this._notebookKernelService.registerProxyKernel(proxyKernel); + this._proxyKernels.set(handle, [proxyKernel, combinedDisposable(listener, registration)]); + } + + $updateProxyKernel(handle: number, data: Partial): void { + const tuple = this._proxyKernels.get(handle); + if (tuple) { + tuple[0].update(data); + } + } + // --- execution $createExecution(handle: number, controllerId: string, rawUri: UriComponents, cellHandle: number): void { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index acdf0672025..b5d57211f95 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1134,6 +1134,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I extHostApiDeprecation.report('notebookConcatTextDocument', extension, 'This proposal is not on track for finalization and will be removed.'); return new ExtHostNotebookConcatDocument(extHostNotebookDocuments, extHostDocuments, notebook, selector); }, + createNotebookProxyController(id: string, notebookType: string, label: string, handler: () => vscode.NotebookController | Thenable) { + checkProposedApiEnabled(extension, 'notebookProxyController'); + return extHostNotebookKernels.createNotebookProxyController(extension, id, notebookType, label, handler); + } }; return { diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 86d8dde204b..27b20192f13 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -979,6 +979,17 @@ export interface INotebookKernelDto2 { preloads?: { uri: UriComponents; provides: string[] }[]; } +export interface INotebookProxyKernelDto { + id: string; + notebookType: string; + extensionId: ExtensionIdentifier; + extensionLocation: UriComponents; + label: string; + detail?: string; + description?: string; + kind?: string; +} + export interface ICellExecuteOutputEditDto { editType: CellExecutionUpdateType.Output; append?: boolean; @@ -1004,6 +1015,8 @@ export interface MainThreadNotebookKernelsShape extends IDisposable { $postMessage(handle: number, editorId: string | undefined, message: any): Promise; $addKernel(handle: number, data: INotebookKernelDto2): Promise; $updateKernel(handle: number, data: Partial): void; + $addProxyKernel(handle: number, data: INotebookProxyKernelDto): Promise; + $updateProxyKernel(handle: number, data: Partial): void; $removeKernel(handle: number): void; $updateNotebookPriority(handle: number, uri: UriComponents, value: number | undefined): void; @@ -2097,6 +2110,7 @@ export interface ExtHostNotebookKernelsShape { $cancelCells(handle: number, uri: UriComponents, handles: number[]): Promise; $acceptKernelMessageFromRenderer(handle: number, editorId: string, message: any): void; $cellExecutionChanged(uri: UriComponents, cellHandle: number, state: notebookCommon.NotebookCellExecutionState | undefined): void; + $resolveKernel(handle: number): Promise; } export interface ExtHostInteractiveShape { diff --git a/src/vs/workbench/api/common/extHostNotebookKernels.ts b/src/vs/workbench/api/common/extHostNotebookKernels.ts index 05550b53475..57486a80e0d 100644 --- a/src/vs/workbench/api/common/extHostNotebookKernels.ts +++ b/src/vs/workbench/api/common/extHostNotebookKernels.ts @@ -12,7 +12,7 @@ import { ResourceMap } from 'vs/base/common/map'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; -import { ExtHostNotebookKernelsShape, ICellExecuteUpdateDto, IMainContext, INotebookKernelDto2, MainContext, MainThreadNotebookKernelsShape, NotebookOutputDto } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostNotebookKernelsShape, ICellExecuteUpdateDto, IMainContext, INotebookKernelDto2, INotebookProxyKernelDto, MainContext, MainThreadNotebookKernelsShape, NotebookOutputDto } from 'vs/workbench/api/common/extHost.protocol'; import { ApiCommand, ApiCommandArgument, ApiCommandResult, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; @@ -34,6 +34,13 @@ interface IKernelData { associatedNotebooks: ResourceMap; } +interface IProxyKernelData { + extensionId: ExtensionIdentifier; + controller: vscode.NotebookProxyController; + onDidChangeSelection: Emitter<{ selected: boolean; notebook: vscode.NotebookDocument }>; + associatedNotebooks: ResourceMap; +} + type ExtHostSelectKernelArgs = ControllerInfo | { notebookEditor: vscode.NotebookEditor } | ControllerInfo & { notebookEditor: vscode.NotebookEditor } | undefined; export type SelectKernelReturnArgs = ControllerInfo | { notebookEditorId: string } | ControllerInfo & { notebookEditorId: string } | undefined; type ControllerInfo = { id: string; extension: string }; @@ -45,6 +52,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { private readonly _activeExecutions = new ResourceMap(); private readonly _kernelData = new Map(); + private readonly _proxyKernelData: Map = new Map(); private _handlePool: number = 0; private readonly _onDidChangeCellExecutionState = new Emitter(); @@ -257,8 +265,105 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { return controller; } + createNotebookProxyController(extension: IExtensionDescription, id: string, viewType: string, label: string, handler: () => vscode.NotebookController | Thenable): vscode.NotebookProxyController { + const handle = this._handlePool++; + + let isDisposed = false; + const commandDisposables = new DisposableStore(); + const onDidChangeSelection = new Emitter<{ selected: boolean; notebook: vscode.NotebookDocument }>(); + + const data: INotebookProxyKernelDto = { + id: createKernelId(extension, id), + notebookType: viewType, + extensionId: extension.identifier, + extensionLocation: extension.extensionLocation, + label: label || extension.identifier.value, + }; + + let _resolveHandler = handler; + + this._proxy.$addProxyKernel(handle, data).catch(err => { + // this can happen when a kernel with that ID is already registered + console.log(err); + isDisposed = true; + }); + + let tokenPool = 0; + const _update = () => { + if (isDisposed) { + return; + } + const myToken = ++tokenPool; + Promise.resolve().then(() => { + if (myToken === tokenPool) { + this._proxy.$updateProxyKernel(handle, data); + } + }); + }; + + // notebook documents that are associated to this controller + const associatedNotebooks = new ResourceMap(); + + const controller: vscode.NotebookProxyController = { + get id() { return id; }, + get notebookType() { return data.notebookType; }, + onDidChangeSelectedNotebooks: onDidChangeSelection.event, + get label() { + return data.label; + }, + set label(value) { + data.label = value ?? extension.displayName ?? extension.name; + _update(); + }, + get detail() { + return data.detail ?? ''; + }, + set detail(value) { + data.detail = value; + _update(); + }, + get description() { + return data.description ?? ''; + }, + set description(value) { + data.description = value; + _update(); + }, + get kind() { + checkProposedApiEnabled(extension, 'notebookControllerKind'); + return data.kind ?? ''; + }, + set kind(value) { + checkProposedApiEnabled(extension, 'notebookControllerKind'); + data.kind = value; + _update(); + }, + get resolveHandler() { + return _resolveHandler; + }, + dispose: () => { + if (!isDisposed) { + this._logService.trace(`NotebookController[${handle}], DISPOSED`); + isDisposed = true; + this._kernelData.delete(handle); + commandDisposables.dispose(); + onDidChangeSelection.dispose(); + this._proxy.$removeKernel(handle); + } + } + }; + + this._proxyKernelData.set(handle, { + extensionId: extension.identifier, + controller, + onDidChangeSelection, + associatedNotebooks + }); + return controller; + } + $acceptNotebookAssociation(handle: number, uri: UriComponents, value: boolean): void { - const obj = this._kernelData.get(handle); + const obj = this._kernelData.get(handle) ?? this._proxyKernelData.get(handle); if (obj) { // update data structure const notebook = this._extHostNotebook.getNotebookDocument(URI.revive(uri))!; @@ -346,6 +451,28 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { } } + async $resolveKernel(handle: number): Promise { + const obj = this._proxyKernelData.get(handle); + if (!obj) { + // extension can dispose kernels in the meantime + return null; + } + + const controller = await obj.controller.resolveHandler(); + let matchedKernelData: IKernelData | undefined; + this._kernelData.forEach(d => { + if (d.controller.id === controller.id) { + matchedKernelData = d; + } + }); + + if (matchedKernelData) { + return `${matchedKernelData.extensionId.value}/${matchedKernelData.controller.id}`; + } + + return null; + } + // --- _createNotebookCellExecution(cell: vscode.NotebookCell, controllerId: string): vscode.NotebookCellExecution { diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts index 83b35ce916d..c93c6b17ba2 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts @@ -197,6 +197,11 @@ registerAction2(class extends Action2 { quickPickItems.push(...suggestions.map(toQuickPick)); } + quickPickItems.push({ + id: 'install', + label: nls.localize('installKernels', "Install kernels from the marketplace"), + }); + // Next display all of the kernels grouped by categories or extensions. // If we don't have a kind, always display those at the bottom. const picks = all.filter(item => !suggestions.includes(item)).map(toQuickPick); diff --git a/src/vs/workbench/contrib/notebook/browser/controller/apiActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/apiActions.ts index d0c6379a54b..68d9d0f8dcc 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/apiActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/apiActions.ts @@ -7,7 +7,7 @@ import * as glob from 'vs/base/common/glob'; import { URI, UriComponents } from 'vs/base/common/uri'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { isDocumentExcludePattern, TransientCellMetadata, TransientDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernelService, IResolvedNotebookKernel, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; CommandsRegistry.registerCommand('_resolveNotebookContentProvider', (accessor): { @@ -66,13 +66,13 @@ CommandsRegistry.registerCommand('_resolveNotebookKernels', async (accessor, arg const uri = URI.revive(args.uri as UriComponents); const kernels = notebookKernelService.getMatchingKernel({ uri, viewType: args.viewType }); - return kernels.all.map(provider => ({ + return kernels.all.filter(kernel => kernel.type === NotebookKernelType.Resolved).map((provider) => ({ id: provider.id, label: provider.label, kind: provider.kind, description: provider.description, detail: provider.detail, isPreferred: false, // todo@jrieken,@rebornix - preloads: provider.preloadUris, + preloads: (provider as IResolvedNotebookKernel).preloadUris, })); }); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index aa1bcc7853f..bafa9981455 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -76,7 +76,7 @@ import { CellKind, INotebookSearchOptions, SelectionStateType } from 'vs/workben import { NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import { INotebookExecutionService } from 'vs/workbench/contrib/notebook/common/notebookExecutionService'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; -import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernelService, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { NotebookOptions, OutputInnerContainerTopPadding } from 'vs/workbench/contrib/notebook/common/notebookOptions'; import { mark } from 'vs/workbench/contrib/notebook/common/notebookPerformance'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; @@ -2108,6 +2108,14 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return; } const { selected } = this.notebookKernelService.getMatchingKernel(this.textModel); + if (!selected) { + return; + } + + if (selected.type === NotebookKernelType.Proxy) { + return; + } + if (!this._webview?.isResolved()) { await this._resolveWebview(); } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts index 000c7b3b72c..b8877a553a2 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts @@ -12,7 +12,7 @@ import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/mode import { CellKind, INotebookTextModel, NotebookCellExecutionState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookExecutionService } from 'vs/workbench/contrib/notebook/common/notebookExecutionService'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; -import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernelService, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; export class NotebookExecutionService implements INotebookExecutionService { declare _serviceBrand: undefined; @@ -45,6 +45,23 @@ export class NotebookExecutionService implements INotebookExecutionService { return; } + if (kernel.type === NotebookKernelType.Proxy) { + // we should actually resolve the kernel + const resolved = await kernel.resolveKernel(notebook.uri); + let kernels = this._notebookKernelService.getMatchingKernel(notebook); + const newlyMatchedKernel = kernels.all.find(k => k.id === resolved); + + if (!newlyMatchedKernel) { + return; + } + + kernel = newlyMatchedKernel; + } + + if (kernel.type === NotebookKernelType.Proxy) { + return; + } + const executeCells: NotebookCellTextModel[] = []; for (const cell of cellsArr) { const cellExe = this._notebookExecutionStateService.getCellExecution(cell.uri); @@ -75,6 +92,11 @@ export class NotebookExecutionService implements INotebookExecutionService { this._logService.debug(`NotebookExecutionService#cancelNotebookCellHandles ${JSON.stringify(cellsArr)}`); const kernel = this._notebookKernelService.getSelectedOrSuggestedKernel(notebook); if (kernel) { + if (kernel.type === NotebookKernelType.Proxy) { + // we should handle cancelling proxy kernel too + return; + } + await kernel.cancelNotebookCellExecution(notebook.uri, cellsArr); } } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts index 1f2e2e72032..09e75953d84 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts @@ -6,7 +6,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { INotebookTextModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { INotebookKernel, ISelectedNotebooksChangeEvent, INotebookKernelMatchResult, INotebookKernelService, INotebookTextModelLike } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, ISelectedNotebooksChangeEvent, INotebookKernelMatchResult, INotebookKernelService, INotebookTextModelLike, INotebookProxyKernel } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { LRUCache, ResourceMap } from 'vs/base/common/map'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { URI } from 'vs/base/common/uri'; @@ -164,6 +164,32 @@ export class NotebookKernelService extends Disposable implements INotebookKernel }); } + registerProxyKernel(kernel: INotebookProxyKernel): IDisposable { + if (this._kernels.has(kernel.id)) { + throw new Error(`NOTEBOOK CONTROLLER with id '${kernel.id}' already exists`); + } + + this._kernels.set(kernel.id, new KernelInfo(kernel)); + this._onDidAddKernel.fire(kernel); + + // auto associate the new kernel to existing notebooks it was + // associated to in the past. + for (const notebook of this._notebookService.getNotebookTextModels()) { + this._tryAutoBindNotebook(notebook, kernel); + } + + return toDisposable(() => { + if (this._kernels.delete(kernel.id)) { + this._onDidRemoveKernel.fire(kernel); + } + for (const [key, candidate] of Array.from(this._notebookBindings)) { + if (candidate === kernel.id) { + this._onDidChangeNotebookKernelBinding.fire({ notebook: NotebookTextModelLikeId.obj(key).uri, oldKernel: kernel.id, newKernel: undefined }); + } + } + }); + } + getMatchingKernel(notebook: INotebookTextModelLike): INotebookKernelMatchResult { // all applicable kernels diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution.ts index d4745c02a43..eb6e260ec10 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution.ts @@ -9,6 +9,7 @@ import { ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/no import { CellViewModelStateChangeEvent } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; import { CellPart } from 'vs/workbench/contrib/notebook/browser/view/cellPart'; import { NotebookCellInternalMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; export class CellExecutionPart extends CellPart { private kernelDisposables = this._register(new DisposableStore()); @@ -41,7 +42,7 @@ export class CellExecutionPart extends CellPart { } private updateExecutionOrder(internalMetadata: NotebookCellInternalMetadata): void { - if (this._notebookEditor.activeKernel?.implementsExecutionOrder) { + if (this._notebookEditor.activeKernel?.type === NotebookKernelType.Resolved && this._notebookEditor.activeKernel?.implementsExecutionOrder) { const executionOrderLabel = typeof internalMetadata.executionOrder === 'number' ? `[${internalMetadata.executionOrder}]` : '[ ]'; 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 7306836ac30..7ed12d9bb09 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts @@ -31,7 +31,7 @@ import { CodeCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/vi import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { CellUri, IOrderedMimeType, NotebookCellOutputsSplice, RENDERER_NOT_AVAILABLE } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { INotebookKernel } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; @@ -127,7 +127,8 @@ export class CellOutputElement extends Disposable { this.notebookEditor.hasModel() && this.innerContainer && this.renderResult && - this.renderResult.type === RenderOutputType.Extension + this.renderResult.type === RenderOutputType.Extension && + this.notebookEditor.activeKernel?.type === NotebookKernelType.Resolved ) { // Output rendered by extension renderer got an update const [mimeTypes, pick] = this.output.resolveMimeTypes(this.notebookEditor.textModel, this.notebookEditor.activeKernel?.preloadProvides); @@ -191,7 +192,7 @@ export class CellOutputElement extends Disposable { const notebookTextModel = this.notebookEditor.textModel; - const [mimeTypes, pick] = this.output.resolveMimeTypes(notebookTextModel, this.notebookEditor.activeKernel?.preloadProvides); + const [mimeTypes, pick] = this.output.resolveMimeTypes(notebookTextModel, this.notebookEditor.activeKernel?.type === NotebookKernelType.Resolved ? this.notebookEditor.activeKernel?.preloadProvides : undefined); if (!mimeTypes.find(mimeType => mimeType.isTrusted) || mimeTypes.length === 0) { this.viewCell.updateOutputHeight(index, 0, 'CellOutputElement#noMimeType'); @@ -299,7 +300,7 @@ export class CellOutputElement extends Disposable { } private async _pickActiveMimeTypeRenderer(outputItemDiv: HTMLElement, notebookTextModel: NotebookTextModel, kernel: INotebookKernel | undefined, viewModel: ICellOutputViewModel) { - const [mimeTypes, currIndex] = viewModel.resolveMimeTypes(notebookTextModel, kernel?.preloadProvides); + const [mimeTypes, currIndex] = viewModel.resolveMimeTypes(notebookTextModel, kernel?.type === NotebookKernelType.Resolved ? kernel?.preloadProvides : undefined); const items: IMimeTypeRenderer[] = []; const unsupportedItems: IMimeTypeRenderer[] = []; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 7bda661ed4e..669bcb2a519 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -37,7 +37,7 @@ import { preloadsScriptStr, RendererMetadata } from 'vs/workbench/contrib/notebo import { transformWebviewThemeVars } from 'vs/workbench/contrib/notebook/browser/view/renderers/webviewThemeMapping'; import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; import { CellUri, INotebookRendererInfo, NotebookSetting, RendererMessagingSpec } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { INotebookKernel } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, IResolvedNotebookKernel, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { IScopedRendererMessaging } from 'vs/workbench/contrib/notebook/common/notebookRendererMessagingService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IWebviewElement, IWebviewService, WebviewContentPurpose } from 'vs/workbench/contrib/webview/browser/webview'; @@ -897,7 +897,7 @@ var requirejs = (function() { } this._preloadsCache.clear(); - if (this._currentKernel) { + if (this._currentKernel && this._currentKernel.type === NotebookKernelType.Resolved) { this._updatePreloadsFromKernel(this._currentKernel); } @@ -1394,14 +1394,14 @@ var requirejs = (function() { const previousKernel = this._currentKernel; this._currentKernel = kernel; - if (previousKernel && previousKernel.preloadUris.length > 0) { + if (previousKernel && previousKernel.type === NotebookKernelType.Resolved && previousKernel.preloadUris.length > 0) { this.webview?.reload(); // preloads will be restored after reload - } else if (kernel) { + } else if (kernel?.type === NotebookKernelType.Resolved) { this._updatePreloadsFromKernel(kernel); } } - private _updatePreloadsFromKernel(kernel: INotebookKernel) { + private _updatePreloadsFromKernel(kernel: IResolvedNotebookKernel) { const resources: IControllerPreload[] = []; for (const preload of kernel.preloadUris) { const uri = this.environmentService.isExtensionDevelopment && (preload.scheme === 'http' || preload.scheme === 'https') @@ -1427,7 +1427,7 @@ var requirejs = (function() { const mixedResourceRoots = [ ...(this.localResourceRootsCache || []), - ...(this._currentKernel ? [this._currentKernel.localResourceRoot] : []), + ...(this._currentKernel && this._currentKernel.type === NotebookKernelType.Resolved ? [this._currentKernel.localResourceRoot] : []), ]; this.webview.localResourcesRoot = mixedResourceRoots; diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys.ts index a90477db542..b1cf70a4946 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorWidgetContextKeys.ts @@ -8,7 +8,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { ICellViewModel, INotebookEditorDelegate, KERNEL_EXTENSIONS } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NOTEBOOK_CELL_TOOLBAR_LOCATION, NOTEBOOK_HAS_OUTPUTS, NOTEBOOK_HAS_RUNNING_CELL, NOTEBOOK_INTERRUPTIBLE_KERNEL, NOTEBOOK_KERNEL, NOTEBOOK_KERNEL_COUNT, NOTEBOOK_KERNEL_SELECTED, NOTEBOOK_MISSING_KERNEL_EXTENSION, NOTEBOOK_USE_CONSOLIDATED_OUTPUT_BUTTON, NOTEBOOK_VIEW_TYPE } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; -import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernelService, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; export class NotebookEditorContextKeys { @@ -148,7 +148,7 @@ export class NotebookEditorContextKeys { const { selected, all } = this._notebookKernelService.getMatchingKernel(this._editor.textModel); this._notebookKernelCount.set(all.length); - this._interruptibleKernel.set(selected?.implementsInterrupt ?? false); + this._interruptibleKernel.set((selected?.type === NotebookKernelType.Resolved && selected.implementsInterrupt) ?? false); this._notebookKernelSelected.set(Boolean(selected)); this._notebookKernel.set(selected?.id ?? ''); } diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts index dc9a494193e..41e1ec74773 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts @@ -9,14 +9,16 @@ import { Action, IAction } from 'vs/base/common/actions'; import { localize } from 'vs/nls'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { selectKernelIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; -import { INotebookKernelMatchResult, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernelMatchResult, INotebookKernelService, ProxyKernelState } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { Event } from 'vs/base/common/event'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { DisposableStore } from 'vs/base/common/lifecycle'; export class NotebooKernelActionViewItem extends ActionViewItem { private _kernelLabel?: HTMLAnchorElement; + private _kernelDisposable: DisposableStore; constructor( actualAction: IAction, @@ -31,6 +33,7 @@ export class NotebooKernelActionViewItem extends ActionViewItem { this._register(_editor.onDidChangeModel(this._update, this)); this._register(_notebookKernelService.onDidChangeNotebookAffinity(this._update, this)); this._register(_notebookKernelService.onDidChangeSelectedNotebooks(this._update, this)); + this._kernelDisposable = this._register(new DisposableStore()); } override render(container: HTMLElement): void { @@ -63,9 +66,9 @@ export class NotebooKernelActionViewItem extends ActionViewItem { } private _updateActionFromKernelInfo(info: INotebookKernelMatchResult): void { - + this._kernelDisposable.clear(); this._action.enabled = true; - const selectedOrSuggested = info.selected ?? (info.all.length === 1 && info.suggestions.length === 1 ? info.suggestions[0] : undefined); + const selectedOrSuggested = info.selected ?? ((info.all.length === 1 && info.suggestions.length === 1 && !('resolveKernel' in info.suggestions[0])) ? info.suggestions[0] : undefined); if (selectedOrSuggested) { // selected or suggested kernel this._action.label = selectedOrSuggested.label; @@ -74,6 +77,24 @@ export class NotebooKernelActionViewItem extends ActionViewItem { // special UI for selected kernel? } + if ('resolveKernel' in selectedOrSuggested) { + if (selectedOrSuggested.connectionState === ProxyKernelState.Initializing) { + this._action.label = localize('initializing', "Initializing..."); + } else { + this._action.label = selectedOrSuggested.label; + } + + this._kernelDisposable.add(selectedOrSuggested.onDidChange(e => { + if (e.connectionState) { + if (selectedOrSuggested.connectionState === ProxyKernelState.Initializing) { + this._action.label = localize('initializing', "Initializing..."); + } else { + this._action.label = selectedOrSuggested.label; + } + } + })); + } + } else { // many kernels or no kernels this._action.label = localize('select', "Select Kernel"); diff --git a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts index 6610fe7177d..19ee2cf2b66 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts @@ -31,8 +31,13 @@ export interface INotebookKernelChangeEvent { hasExecutionOrder?: true; } -export interface INotebookKernel { +export const enum NotebookKernelType { + Resolved, + Proxy = 1 +} +export interface IResolvedNotebookKernel { + readonly type: NotebookKernelType.Resolved; readonly id: string; readonly viewType: string; readonly onDidChange: Event>; @@ -54,6 +59,33 @@ export interface INotebookKernel { cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise; } +export const enum ProxyKernelState { + Disconnected = 1, + Connected = 2, + Initializing = 3 +} + +export interface INotebookProxyKernelChangeEvent extends INotebookKernelChangeEvent { + connectionState?: true; +} + +export interface INotebookProxyKernel { + readonly type: NotebookKernelType.Proxy; + readonly id: string; + readonly viewType: string; + readonly extension: ExtensionIdentifier; + readonly onDidChange: Event>; + label: string; + description?: string; + detail?: string; + kind?: string; + supportedLanguages: string[]; + connectionState: ProxyKernelState; + resolveKernel(uri: URI): Promise; +} + +export type INotebookKernel = IResolvedNotebookKernel | INotebookProxyKernel; + export interface INotebookTextModelLike { uri: URI; viewType: string } export const INotebookKernelService = createDecorator('INotebookKernelService'); @@ -66,7 +98,8 @@ export interface INotebookKernelService { readonly onDidChangeSelectedNotebooks: Event; readonly onDidChangeNotebookAffinity: Event; - registerKernel(kernel: INotebookKernel): IDisposable; + registerKernel(kernel: IResolvedNotebookKernel): IDisposable; + registerProxyKernel(proxyKernel: INotebookProxyKernel): IDisposable; getMatchingKernel(notebook: INotebookTextModelLike): INotebookKernelMatchResult; diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts index 6f65268830f..8d3bff7a83a 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts @@ -20,7 +20,7 @@ import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewMod import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { CellKind, IOutputDto, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; -import { INotebookKernel, INotebookKernelService, ISelectedNotebooksChangeEvent } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernelService, IResolvedNotebookKernel, ISelectedNotebooksChangeEvent, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { setupInstantiationService, withTestNotebook as _withTestNotebook } from 'vs/workbench/contrib/notebook/test/browser/testNotebookEditor'; @@ -166,7 +166,8 @@ suite('NotebookExecutionService', () => { }); }); -class TestNotebookKernel implements INotebookKernel { +class TestNotebookKernel implements IResolvedNotebookKernel { + type: NotebookKernelType.Resolved = NotebookKernelType.Resolved; id: string = 'test'; label: string = ''; viewType = '*'; diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts index 887a4a05443..e5b7f84b8bf 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts @@ -21,7 +21,7 @@ import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/no import { CellEditType, CellKind, CellUri, IOutputDto, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookExecutionService } from 'vs/workbench/contrib/notebook/common/notebookExecutionService'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; -import { INotebookKernel, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernelService, IResolvedNotebookKernel, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { setupInstantiationService, withTestNotebook as _withTestNotebook } from 'vs/workbench/contrib/notebook/test/browser/testNotebookEditor'; @@ -170,7 +170,8 @@ suite('NotebookExecutionStateService', () => { }); }); -class TestNotebookKernel implements INotebookKernel { +class TestNotebookKernel implements IResolvedNotebookKernel { + type: NotebookKernelType.Resolved = NotebookKernelType.Resolved; id: string = 'test'; label: string = ''; viewType = '*'; diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookKernelService.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookKernelService.test.ts index b1ebabc1db2..62d6afecb8b 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookKernelService.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookKernelService.test.ts @@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { setupInstantiationService, withTestNotebook as _withTestNotebook } from 'vs/workbench/contrib/notebook/test/browser/testNotebookEditor'; import { Emitter, Event } from 'vs/base/common/event'; -import { INotebookKernel, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernelService, IResolvedNotebookKernel, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { NotebookKernelService } from 'vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { mock } from 'vs/base/test/common/mock'; @@ -159,7 +159,8 @@ suite('NotebookKernelService', () => { }); }); -class TestNotebookKernel implements INotebookKernel { +class TestNotebookKernel implements IResolvedNotebookKernel { + type: NotebookKernelType.Resolved = NotebookKernelType.Resolved; id: string = Math.random() + 'kernel'; label: string = 'test-label'; viewType = '*'; diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 0a1e93cded5..4bb2426b9a3 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -42,6 +42,7 @@ export const allApiProposals = Object.freeze({ notebookLiveShare: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookLiveShare.d.ts', notebookMessaging: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookMessaging.d.ts', notebookMime: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookMime.d.ts', + notebookProxyController: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookProxyController.d.ts', portsAttributes: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.portsAttributes.d.ts', 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', diff --git a/src/vscode-dts/vscode.proposed.notebookProxyController.d.ts b/src/vscode-dts/vscode.proposed.notebookProxyController.d.ts new file mode 100644 index 00000000000..5d873192c4a --- /dev/null +++ b/src/vscode-dts/vscode.proposed.notebookProxyController.d.ts @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + + export interface NotebookProxyController { + /** + * The identifier of this notebook controller. + * + * _Note_ that controllers are remembered by their identifier and that extensions should use + * stable identifiers across sessions. + */ + readonly id: string; + + /** + * The notebook type this controller is for. + */ + readonly notebookType: string; + + /** + * The human-readable label of this notebook controller. + */ + label: string; + + /** + * The human-readable description which is rendered less prominent. + */ + description?: string; + + /** + * The human-readable detail which is rendered less prominent. + */ + detail?: string; + + /** + * The human-readable label used to categorise controllers. + */ + kind?: string; + + resolveHandler: () => NotebookController | Thenable; + + readonly onDidChangeSelectedNotebooks: Event<{ readonly notebook: NotebookDocument; readonly selected: boolean }>; + + /** + * Dispose and free associated resources. + */ + dispose(): void; + } + + export namespace notebooks { + export function createNotebookProxyController(id: string, notebookType: string, label: string, resolveHandler: () => NotebookController | Thenable): NotebookProxyController; + } +} From 4f4bf424242f3fee64cf241b1b5f735a2f86585d Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 12 Apr 2022 15:13:45 +0200 Subject: [PATCH 004/245] fixes #146658 --- src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts index 79f89381904..0af76e3b6a0 100644 --- a/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts +++ b/src/vs/base/browser/ui/dropdown/dropdownActionViewItem.ts @@ -88,6 +88,7 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem { this.element.setAttribute('aria-haspopup', 'true'); this.element.setAttribute('aria-expanded', 'false'); this.element.title = this._action.label || ''; + this.element.ariaLabel = this._action.label || ''; return null; }; From 8f5b743a0b459b1a63dd2753112c23ee015a0a5e Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 12 Apr 2022 15:33:14 +0200 Subject: [PATCH 005/245] [json] Allow "json.validate.enable": false in settings / disable JSON validation or error checking. Fixes #114775 --- .../server/src/htmlServer.ts | 2 +- .../client/src/jsonClient.ts | 7 +++- .../json-language-features/package.json | 10 ++++- .../json-language-features/package.nls.json | 1 + .../json-language-features/server/README.md | 2 + .../server/package.json | 2 +- .../server/src/jsonServer.ts | 41 +++++++++++-------- .../json-language-features/server/yarn.lock | 8 ++-- extensions/json-language-features/yarn.lock | 16 ++++---- 9 files changed, 55 insertions(+), 34 deletions(-) diff --git a/extensions/html-language-features/server/src/htmlServer.ts b/extensions/html-language-features/server/src/htmlServer.ts index b0df59821b8..6713556ae38 100644 --- a/extensions/html-language-features/server/src/htmlServer.ts +++ b/extensions/html-language-features/server/src/htmlServer.ts @@ -67,7 +67,7 @@ namespace SemanticTokenLegendRequest { export interface RuntimeEnvironment { fileFs?: FileSystemProvider; - configureHttpRequests?(proxy: string, strictSSL: boolean): void; + configureHttpRequests?(proxy: string | undefined, strictSSL: boolean): void; readonly timer: { setImmediate(callback: (...args: any[]) => void, ...args: any[]): Disposable; setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): Disposable; diff --git a/extensions/json-language-features/client/src/jsonClient.ts b/extensions/json-language-features/client/src/jsonClient.ts index 1044fa22599..9bf63477ebe 100644 --- a/extensions/json-language-features/client/src/jsonClient.ts +++ b/extensions/json-language-features/client/src/jsonClient.ts @@ -59,7 +59,8 @@ namespace ResultLimitReachedNotification { interface Settings { json?: { schemas?: JSONSchemaSettings[]; - format?: { enable: boolean }; + format?: { enable?: boolean }; + validate?: { enable?: boolean }; resultLimit?: number; }; http?: { @@ -76,6 +77,7 @@ export interface JSONSchemaSettings { namespace SettingIds { export const enableFormatter = 'json.format.enable'; + export const enableValidation = 'json.validate.enable'; export const enableSchemaDownload = 'json.schemaDownload.enable'; export const maxItemsComputed = 'json.maxItemsComputed'; } @@ -425,6 +427,7 @@ function getSchemaAssociations(_context: ExtensionContext): ISchemaAssociation[] } function getSettings(): Settings { + const configuration = workspace.getConfiguration(); const httpSettings = workspace.getConfiguration('http'); const resultLimit: number = Math.trunc(Math.max(0, Number(workspace.getConfiguration().get(SettingIds.maxItemsComputed)))) || 5000; @@ -435,6 +438,8 @@ function getSettings(): Settings { proxyStrictSSL: httpSettings.get('proxyStrictSSL') }, json: { + validate: { enable: configuration.get(SettingIds.enableValidation) }, + format: { enable: configuration.get(SettingIds.enableFormatter) }, schemas: [], resultLimit } diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index d82ebaa85f5..25aef750a76 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -73,6 +73,12 @@ } } }, + "json.validate.enable": { + "type": "boolean", + "scope": "window", + "default": true, + "description": "%json.validate.enable.desc%" + }, "json.format.enable": { "type": "boolean", "scope": "window", @@ -141,8 +147,8 @@ ] }, "dependencies": { - "request-light": "^0.5.7", - "@vscode/extension-telemetry": "0.4.10", + "@vscode/extension-telemetry": "0.5.0", + "request-light": "^0.5.8", "vscode-languageclient": "^7.0.0", "vscode-nls": "^5.0.0" }, diff --git a/extensions/json-language-features/package.nls.json b/extensions/json-language-features/package.nls.json index f83dd588ecd..8afec56a90f 100644 --- a/extensions/json-language-features/package.nls.json +++ b/extensions/json-language-features/package.nls.json @@ -7,6 +7,7 @@ "json.schemas.fileMatch.item.desc": "A file pattern that can contain '*' to match against when resolving JSON files to schemas.", "json.schemas.schema.desc": "The schema definition for the given URL. The schema only needs to be provided to avoid accesses to the schema URL.", "json.format.enable.desc": "Enable/disable default JSON formatter", + "json.validate.enable.desc": "Enable/disable JSON validation.", "json.tracing.desc": "Traces the communication between VS Code and the JSON language server.", "json.colorDecorators.enable.desc": "Enables or disables color decorators", "json.colorDecorators.enable.deprecationMessage": "The setting `json.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`.", diff --git a/extensions/json-language-features/server/README.md b/extensions/json-language-features/server/README.md index 328a523f5a2..e82ae06d776 100644 --- a/extensions/json-language-features/server/README.md +++ b/extensions/json-language-features/server/README.md @@ -62,6 +62,8 @@ The server supports the following settings: - json - `format` - `enable`: Whether the server should register the formatting support. This option is only applicable if the client supports *dynamicRegistration* for *rangeFormatting* and `initializationOptions.provideFormatter` is not defined. + - `validate` + - `enable`: Whether the server should validate. Defaults to `true` if not set. - `schemas`: Configures association of file names to schema URL or schemas and/or associations of schema URL to schema content. - `fileMatch`: an array of file names or paths (separated by `/`). `*` can be used as a wildcard. Exclusion patterns can also be defined and start with '!'. A file matches when there is at least one matching pattern and the last matching pattern is not an exclusion pattern. - `url`: The URL of the schema, optional when also a schema is provided. diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index cb3fd6c2c78..cdb3d80ddd9 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -13,7 +13,7 @@ "main": "./out/node/jsonServerMain", "dependencies": { "jsonc-parser": "^3.0.0", - "request-light": "^0.5.7", + "request-light": "^0.5.8", "vscode-json-languageservice": "^4.2.1", "vscode-languageserver": "^7.0.0", "vscode-uri": "^3.0.3" diff --git a/extensions/json-language-features/server/src/jsonServer.ts b/extensions/json-language-features/server/src/jsonServer.ts index a44baf31f30..e105859371f 100644 --- a/extensions/json-language-features/server/src/jsonServer.ts +++ b/extensions/json-language-features/server/src/jsonServer.ts @@ -57,7 +57,7 @@ export interface RequestService { export interface RuntimeEnvironment { file?: RequestService; http?: RequestService; - configureHttpRequests?(proxy: string, strictSSL: boolean): void; + configureHttpRequests?(proxy: string | undefined, strictSSL: boolean): void; readonly timer: { setImmediate(callback: (...args: any[]) => void, ...args: any[]): Disposable; setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): Disposable; @@ -166,14 +166,15 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) // The settings interface describes the server relevant settings part interface Settings { - json: { - schemas: JSONSchemaSettings[]; - format: { enable: boolean }; + json?: { + schemas?: JSONSchemaSettings[]; + format?: { enable?: boolean }; + validate?: { enable?: boolean }; resultLimit?: number; }; - http: { - proxy: string; - proxyStrictSSL: boolean; + http?: { + proxy?: string; + proxyStrictSSL?: boolean; }; } @@ -226,22 +227,24 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) let jsonConfigurationSettings: JSONSchemaSettings[] | undefined = undefined; let schemaAssociations: ISchemaAssociations | SchemaConfiguration[] | undefined = undefined; let formatterRegistrations: Thenable[] | null = null; + let validateEnabled = true; // The settings have changed. Is send on server activation as well. connection.onDidChangeConfiguration((change) => { let settings = change.settings; if (runtime.configureHttpRequests) { - runtime.configureHttpRequests(settings.http && settings.http.proxy, settings.http && settings.http.proxyStrictSSL); + runtime.configureHttpRequests(settings?.http?.proxy, !!settings.http?.proxyStrictSSL); } - jsonConfigurationSettings = settings.json && settings.json.schemas; + jsonConfigurationSettings = settings.json?.schemas; + validateEnabled = !!settings.json?.validate?.enable; updateConfiguration(); - foldingRangeLimit = Math.trunc(Math.max(settings.json && settings.json.resultLimit || foldingRangeLimitDefault, 0)); - resultLimit = Math.trunc(Math.max(settings.json && settings.json.resultLimit || Number.MAX_VALUE, 0)); + foldingRangeLimit = Math.trunc(Math.max(settings.json?.resultLimit || foldingRangeLimitDefault, 0)); + resultLimit = Math.trunc(Math.max(settings.json?.resultLimit || Number.MAX_VALUE, 0)); // dynamically enable & disable the formatter if (dynamicFormatterRegistration) { - const enableFormatter = settings && settings.json && settings.json.format && settings.json.format.enable; + const enableFormatter = settings.json?.format?.enable; if (enableFormatter) { if (!formatterRegistrations) { const documentSelector = [{ language: 'json' }, { language: 'jsonc' }]; @@ -309,7 +312,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) function updateConfiguration() { const languageSettings = { - validate: true, + validate: validateEnabled, allowComments: true, schemas: new Array() }; @@ -371,10 +374,14 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) function triggerValidation(textDocument: TextDocument): void { cleanPendingValidation(textDocument); - pendingValidationRequests[textDocument.uri] = runtime.timer.setTimeout(() => { - delete pendingValidationRequests[textDocument.uri]; - validateTextDocument(textDocument); - }, validationDelayMs); + if (validateEnabled) { + pendingValidationRequests[textDocument.uri] = runtime.timer.setTimeout(() => { + delete pendingValidationRequests[textDocument.uri]; + validateTextDocument(textDocument); + }, validationDelayMs); + } else { + connection.sendDiagnostics({ uri: textDocument.uri, diagnostics: [] }); + } } function validateTextDocument(textDocument: TextDocument, callback?: (diagnostics: Diagnostic[]) => void): void { diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index 483e6cc060a..b43c8aa2131 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -17,10 +17,10 @@ jsonc-parser@^3.0.0: resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.0.0.tgz#abdd785701c7e7eaca8a9ec8cf070ca51a745a22" integrity sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA== -request-light@^0.5.7: - version "0.5.7" - resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.5.7.tgz#1c448c22153b55d2cd278eb414df24a5ad6e6d5e" - integrity sha512-i/wKzvcx7Er8tZnvqSxWuNO5ZGggu2UgZAqj/RyZ0si7lBTXL7kZiI/dWxzxnQjaY7s5HEy1qK21Do4Ncr6cVw== +request-light@^0.5.8: + version "0.5.8" + resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.5.8.tgz#8bf73a07242b9e7b601fac2fa5dc22a094abcc27" + integrity sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg== vscode-json-languageservice@^4.2.1: version "4.2.1" diff --git a/extensions/json-language-features/yarn.lock b/extensions/json-language-features/yarn.lock index 2049e0f32bf..98216da36e6 100644 --- a/extensions/json-language-features/yarn.lock +++ b/extensions/json-language-features/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae" integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w== -"@vscode/extension-telemetry@0.4.10": - version "0.4.10" - resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.4.10.tgz#be960c05bdcbea0933866346cf244acad6cac910" - integrity sha512-XgyUoWWRQExTmd9DynIIUQo1NPex/zIeetdUAXeBjVuW9ioojM1TcDaSqOa/5QLC7lx+oEXwSU1r0XSBgzyz6w== +"@vscode/extension-telemetry@0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.5.0.tgz#8214171e550393d577fc56326fa986c6800b831b" + integrity sha512-27FsgeVJvC4zVw7Ar3Ub+7vJswDt8RoBFpbgBwf8Xq/B2gaT8G6a+gkw3s2pQmjWGIqyu7TRA8e9rS8/vxv6NQ== balanced-match@^1.0.0: version "1.0.0" @@ -44,10 +44,10 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -request-light@^0.5.7: - version "0.5.7" - resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.5.7.tgz#1c448c22153b55d2cd278eb414df24a5ad6e6d5e" - integrity sha512-i/wKzvcx7Er8tZnvqSxWuNO5ZGggu2UgZAqj/RyZ0si7lBTXL7kZiI/dWxzxnQjaY7s5HEy1qK21Do4Ncr6cVw== +request-light@^0.5.8: + version "0.5.8" + resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.5.8.tgz#8bf73a07242b9e7b601fac2fa5dc22a094abcc27" + integrity sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg== semver@^7.3.4: version "7.3.4" From 54686071815ef945119d4db11b76bbdf8c176f98 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 12 Apr 2022 16:02:02 +0200 Subject: [PATCH 006/245] Make npm task path fix backwards compatible Fixes #147193 --- extensions/npm/src/tasks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/npm/src/tasks.ts b/extensions/npm/src/tasks.ts index f4da67b93f2..7ee330d1296 100644 --- a/extensions/npm/src/tasks.ts +++ b/extensions/npm/src/tasks.ts @@ -324,7 +324,7 @@ export async function createTask(packageManager: string, script: NpmTaskDefiniti } let relativePackageJson = getRelativePath(packageJsonUri); - if (relativePackageJson.length) { + if (relativePackageJson.length && !kind.path) { kind.path = relativePackageJson.substring(0, relativePackageJson.length - 1); } let taskName = getTaskName(kind.script, relativePackageJson); From 01cfc62ead7006c228e5c91408f6705e2fc40b51 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 12 Apr 2022 16:49:52 +0200 Subject: [PATCH 007/245] Update shellscript grammar and add new test --- extensions/shellscript/cgmanifest.json | 4 +- .../syntaxes/shell-unix-bash.tmLanguage.json | 66 +++++++++- .../test/colorize-fixtures/test.sh | 5 + .../test/colorize-results/test_sh.json | 120 ++++++++++++++++++ 4 files changed, 190 insertions(+), 5 deletions(-) diff --git a/extensions/shellscript/cgmanifest.json b/extensions/shellscript/cgmanifest.json index fcf7f676fa7..6f0ae3a254e 100644 --- a/extensions/shellscript/cgmanifest.json +++ b/extensions/shellscript/cgmanifest.json @@ -6,12 +6,12 @@ "git": { "name": "atom/language-shellscript", "repositoryUrl": "https://github.com/atom/language-shellscript", - "commitHash": "4c3711edbe8eac6f501976893976b1ac6a043d50" + "commitHash": "4f8d7bb5cc4d1643674551683df10fe552dd5a6f" } }, "license": "MIT", "description": "The file syntaxes/shell-unix-bash.tmLanguage.json was derived from the Atom package https://github.com/atom/language-shellscript which was originally converted from the TextMate bundle https://github.com/textmate/shellscript.tmbundle.", - "version": "0.26.0" + "version": "0.28.2" } ], "version": 1 diff --git a/extensions/shellscript/syntaxes/shell-unix-bash.tmLanguage.json b/extensions/shellscript/syntaxes/shell-unix-bash.tmLanguage.json index 0089e5aa5c4..bfbb5f135d0 100644 --- a/extensions/shellscript/syntaxes/shell-unix-bash.tmLanguage.json +++ b/extensions/shellscript/syntaxes/shell-unix-bash.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/atom/language-shellscript/commit/4c3711edbe8eac6f501976893976b1ac6a043d50", + "version": "https://github.com/atom/language-shellscript/commit/4f8d7bb5cc4d1643674551683df10fe552dd5a6f", "name": "Shell Script", "scopeName": "source.shell", "patterns": [ @@ -624,7 +624,7 @@ ] }, { - "begin": "(<<)-\\s*(\"|'|)\\s*\\\\?([^;&<\\s]+)\\2", + "begin": "(<<)-\\s*(\"|')\\s*\\\\?([^;&<\\s]+)\\2", "beginCaptures": { "1": { "name": "keyword.operator.heredoc.shell" @@ -642,7 +642,7 @@ "name": "string.unquoted.heredoc.no-indent.shell" }, { - "begin": "(<<)\\s*(\"|'|)\\s*\\\\?([^;&<\\s]+)\\2", + "begin": "(<<)\\s*(\"|')\\s*\\\\?([^;&<\\s]+)\\2", "beginCaptures": { "1": { "name": "keyword.operator.heredoc.shell" @@ -658,6 +658,66 @@ } }, "name": "string.unquoted.heredoc.shell" + }, + { + "begin": "(<<)-\\s*\\\\?([^;&<\\s]+)", + "beginCaptures": { + "1": { + "name": "keyword.operator.heredoc.shell" + }, + "2": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "end": "^\\t*(\\2)(?=\\s|;|&|$)", + "endCaptures": { + "1": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "name": "string.unquoted.heredoc.expanded.no-indent.shell", + "patterns": [ + { + "match": "\\\\[\\$`\\\\\\n]", + "name": "constant.character.escape.shell" + }, + { + "include": "#variable" + }, + { + "include": "#interpolation" + } + ] + }, + { + "begin": "(<<)\\s*\\\\?([^;&<\\s]+)", + "beginCaptures": { + "1": { + "name": "keyword.operator.heredoc.shell" + }, + "2": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "end": "^(\\2)(?=\\s|;|&|$)", + "endCaptures": { + "1": { + "name": "keyword.control.heredoc-token.shell" + } + }, + "name": "string.unquoted.heredoc.expanded.shell", + "patterns": [ + { + "match": "\\\\[\\$`\\\\\\n]", + "name": "constant.character.escape.shell" + }, + { + "include": "#variable" + }, + { + "include": "#interpolation" + } + ] } ] }, diff --git a/extensions/vscode-colorize-tests/test/colorize-fixtures/test.sh b/extensions/vscode-colorize-tests/test/colorize-fixtures/test.sh index 4c5bf8f7eab..50751c1e4a7 100644 --- a/extensions/vscode-colorize-tests/test/colorize-fixtures/test.sh +++ b/extensions/vscode-colorize-tests/test/colorize-fixtures/test.sh @@ -10,6 +10,11 @@ fi DEVELOPER=$(xcode-select -print-path) LIPO=$(xcrun -sdk iphoneos -find lipo) +cat <<-EOF > /path/file + # A heredoc with a variable $DEVELOPER + some more file +EOF + function code() { cd $ROOT diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_sh.json b/extensions/vscode-colorize-tests/test/colorize-results/test_sh.json index a755f73ee61..9fd6ba74f7e 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_sh.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_sh.json @@ -1223,6 +1223,126 @@ "hc_light": "string: #A31515" } }, + { + "c": "cat ", + "t": "source.shell", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "hc_light": "default: #292929" + } + }, + { + "c": "<<", + "t": "source.shell string.unquoted.heredoc.expanded.no-indent.shell keyword.operator.heredoc.shell", + "r": { + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4", + "hc_light": "keyword.operator: #000000" + } + }, + { + "c": "-", + "t": "source.shell string.unquoted.heredoc.expanded.no-indent.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #A31515" + } + }, + { + "c": "EOF", + "t": "source.shell string.unquoted.heredoc.expanded.no-indent.shell keyword.control.heredoc-token.shell", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0", + "hc_light": "keyword.control: #AF00DB" + } + }, + { + "c": " > /path/file", + "t": "source.shell string.unquoted.heredoc.expanded.no-indent.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #A31515" + } + }, + { + "c": "\t# A heredoc with a variable ", + "t": "source.shell string.unquoted.heredoc.expanded.no-indent.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #A31515" + } + }, + { + "c": "$", + "t": "source.shell string.unquoted.heredoc.expanded.no-indent.shell variable.other.normal.shell punctuation.definition.variable.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE", + "hc_light": "variable: #001080" + } + }, + { + "c": "DEVELOPER", + "t": "source.shell string.unquoted.heredoc.expanded.no-indent.shell variable.other.normal.shell", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE", + "hc_light": "variable: #001080" + } + }, + { + "c": "\tsome more file", + "t": "source.shell string.unquoted.heredoc.expanded.no-indent.shell", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #A31515" + } + }, + { + "c": "EOF", + "t": "source.shell string.unquoted.heredoc.expanded.no-indent.shell keyword.control.heredoc-token.shell", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0", + "hc_light": "keyword.control: #AF00DB" + } + }, { "c": "function", "t": "source.shell meta.function.shell storage.type.function.shell", From 833947f9d1748e94dfd3bb41ef879d872fed312b Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Tue, 12 Apr 2022 11:37:28 -0400 Subject: [PATCH 008/245] fixes #147070 (#147302) --- src/vs/base/browser/ui/actionbar/actionbar.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index b996cc080cb..2a5548664c0 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -470,7 +470,7 @@ export class ActionBar extends Disposable implements IActionRunner { this.focusedItem = (this.focusedItem + 1) % this.viewItems.length; item = this.viewItems[this.focusedItem]; - } while (this.focusedItem !== startIndex && this.options.focusOnlyEnabledItems && !item.isEnabled()); + } while (this.focusedItem !== startIndex && ((this.options.focusOnlyEnabledItems && !item.isEnabled()) || item.action.id === Separator.ID)); this.updateFocus(); return true; @@ -497,7 +497,7 @@ export class ActionBar extends Disposable implements IActionRunner { this.focusedItem = this.viewItems.length - 1; } item = this.viewItems[this.focusedItem]; - } while (this.focusedItem !== startIndex && this.options.focusOnlyEnabledItems && !item.isEnabled()); + } while (this.focusedItem !== startIndex && ((this.options.focusOnlyEnabledItems && !item.isEnabled()) || item.action.id === Separator.ID)); this.updateFocus(true); @@ -525,6 +525,10 @@ export class ActionBar extends Disposable implements IActionRunner { focusItem = false; } + if (actionViewItem.action.id === Separator.ID) { + focusItem = false; + } + if (!focusItem) { this.actionsList.focus({ preventScroll }); this.previouslyFocusedItem = undefined; From 176257ec89695ce97c2933385862b8e68fd8a94d Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 12 Apr 2022 17:41:27 +0200 Subject: [PATCH 009/245] standalone: toogle hc comamnd should check current theme. For microsoft/monaco-editor#3069 --- .../browser/toggleHighContrast/toggleHighContrast.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast.ts b/src/vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast.ts index 3fce47976a9..d10e1ccafb9 100644 --- a/src/vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast.ts +++ b/src/vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast.ts @@ -7,6 +7,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions'; import { IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneTheme'; import { ToggleHighContrastNLS } from 'vs/editor/common/standaloneStrings'; +import { isHighContrast } from 'vs/platform/theme/common/theme'; class ToggleHighContrast extends EditorAction { @@ -24,9 +25,9 @@ class ToggleHighContrast extends EditorAction { public run(accessor: ServicesAccessor, editor: ICodeEditor): void { const standaloneThemeService = accessor.get(IStandaloneThemeService); - if (this._originalThemeName) { + if (isHighContrast(standaloneThemeService.getColorTheme().type)) { // We must toggle back to the integrator's theme - standaloneThemeService.setTheme(this._originalThemeName); + standaloneThemeService.setTheme(this._originalThemeName || 'vs'); this._originalThemeName = null; } else { this._originalThemeName = standaloneThemeService.getColorTheme().themeName; From 6c9776d05504c487829ab75b1b1f3a0216c486f7 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Tue, 12 Apr 2022 11:45:25 -0400 Subject: [PATCH 010/245] fixes #147042 --- src/vs/workbench/browser/actions/layoutActions.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index aaf84ac8ef7..a82c1828bb8 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -22,7 +22,7 @@ import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/b import { ToggleAuxiliaryBarAction } from 'vs/workbench/browser/parts/auxiliarybar/auxiliaryBarActions'; import { TogglePanelAction } from 'vs/workbench/browser/parts/panel/panelActions'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { AuxiliaryBarVisibleContext, PanelAlignmentContext, PanelVisibleContext, SideBarVisibleContext, FocusedViewContext, InEditorZenModeContext, IsCenteredLayoutContext, EditorAreaVisibleContext, IsFullscreenContext } from 'vs/workbench/common/contextkeys'; +import { AuxiliaryBarVisibleContext, PanelAlignmentContext, PanelVisibleContext, SideBarVisibleContext, FocusedViewContext, InEditorZenModeContext, IsCenteredLayoutContext, EditorAreaVisibleContext, IsFullscreenContext, PanelPositionContext } from 'vs/workbench/common/contextkeys'; import { Codicon } from 'vs/base/common/codicons'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; @@ -330,12 +330,8 @@ registerAction2(class extends Action2 { category: CATEGORIES.View, f1: true, toggled: EditorAreaVisibleContext, - // Remove from appearance menu - // menu: [{ - // id: MenuId.MenubarAppearanceMenu, - // group: '2_workbench_layout', - // order: 5 - // }] + // the workbench grid currently prevents us from supporting panel maximization with non-center panel alignment + precondition: ContextKeyExpr.or(PanelAlignmentContext.isEqualTo('center'), PanelPositionContext.notEqualsTo('bottom')) }); } From 0d78957186ed43264c09240b5900e5470172cea4 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 12 Apr 2022 09:20:23 -0700 Subject: [PATCH 011/245] Update md grammar Fixes #146610 --- extensions/markdown-basics/cgmanifest.json | 2 +- extensions/markdown-basics/syntaxes/markdown.tmLanguage.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/markdown-basics/cgmanifest.json b/extensions/markdown-basics/cgmanifest.json index 933fdc4d314..06f3fb5a14b 100644 --- a/extensions/markdown-basics/cgmanifest.json +++ b/extensions/markdown-basics/cgmanifest.json @@ -33,7 +33,7 @@ "git": { "name": "microsoft/vscode-markdown-tm-grammar", "repositoryUrl": "https://github.com/microsoft/vscode-markdown-tm-grammar", - "commitHash": "a8545b220dc2cb109835571ba3458ff138e97361" + "commitHash": "df4829558048663156abcc4235619c773335445e" } }, "license": "MIT", diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index e91e0f6ea60..fa453cecac4 100644 --- a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json +++ b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/a8545b220dc2cb109835571ba3458ff138e97361", + "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/df4829558048663156abcc4235619c773335445e", "name": "Markdown", "scopeName": "text.html.markdown", "patterns": [ @@ -2918,7 +2918,7 @@ "name": "punctuation.definition.strikethrough.markdown" } }, - "match": "(~+)((?:[^~]|(?!(? Date: Tue, 12 Apr 2022 10:13:41 -0700 Subject: [PATCH 012/245] Use SuggestController to trigger suggest widget instead of command --- .../services/preferences/browser/preferencesService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index eb5737fa9af..8b29a6f2650 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -46,6 +46,7 @@ import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteA import { ITextEditorService } from 'vs/workbench/services/textfile/common/textEditorService'; 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'; const emptyEditableSettingsContent = '{\n}'; @@ -76,7 +77,6 @@ export class PreferencesService extends Disposable implements IPreferencesServic @ILanguageService private readonly languageService: ILanguageService, @ILabelService private readonly labelService: ILabelService, @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, - @ICommandService private readonly commandService: ICommandService, @ITextEditorService private readonly textEditorService: ITextEditorService ) { super(); @@ -542,7 +542,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic codeEditor.revealPositionNearTop(position); codeEditor.focus(); if (edit) { - await this.commandService.executeCommand('editor.action.triggerSuggest'); + SuggestController.get(codeEditor)?.triggerSuggest(); } } } From 841c777ad8637b6bfbbd0a192ed3ffad08756d5d Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 12 Apr 2022 19:32:40 +0200 Subject: [PATCH 013/245] Introduces InlineCompletionList.commands and improves documentation comments. --- src/vs/editor/common/languages.ts | 4 ++ .../browser/ghostTextHoverParticipant.ts | 14 ++++ .../browser/inlineCompletionsModel.ts | 7 +- src/vs/monaco.d.ts | 4 ++ .../api/common/extHostLanguageFeatures.ts | 72 ++++++++++++++----- src/vs/workbench/api/common/extHostTypes.ts | 5 +- .../vscode.proposed.inlineCompletionsNew.d.ts | 70 ++++++++++++------ 7 files changed, 134 insertions(+), 42 deletions(-) diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index 3659925bf75..fedc753fb66 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -833,6 +833,10 @@ export interface InlineCompletion { export interface InlineCompletions { readonly items: readonly TItem[]; + /** + * A list of commands associated with the inline completions of this list. + */ + readonly commands?: Command[]; } export interface InlineCompletionsProvider { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/ghostTextHoverParticipant.ts b/src/vs/editor/contrib/inlineCompletions/browser/ghostTextHoverParticipant.ts index 7ab26922483..6a2fe0eb945 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/ghostTextHoverParticipant.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/ghostTextHoverParticipant.ts @@ -20,6 +20,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { inlineSuggestCommitId } from 'vs/editor/contrib/inlineCompletions/browser/consts'; +import { Command } from 'vs/editor/common/languages'; export class InlineCompletionsHover implements IHoverPart { constructor( @@ -39,6 +40,10 @@ export class InlineCompletionsHover implements IHoverPart { public hasMultipleSuggestions(): Promise { return this.controller.hasMultipleInlineCompletions(); } + + public get commands(): Command[] { + return this.controller.activeModel?.activeInlineCompletionsModel?.completionSession.value?.commands || []; + } } export class InlineCompletionsHoverParticipant implements IEditorHoverParticipant { @@ -100,6 +105,7 @@ export class InlineCompletionsHoverParticipant implements IEditorHoverParticipan this.renderScreenReaderText(context, part, disposableStore); } + // TODO@hediet: deprecate MenuId.InlineCompletionsActions const menu = disposableStore.add(this._menuService.createMenu( MenuId.InlineCompletionsActions, this._contextKeyService @@ -131,6 +137,14 @@ export class InlineCompletionsHoverParticipant implements IEditorHoverParticipan } }); + for (const command of part.commands) { + context.statusBar.addAction({ + label: command.title, + commandId: command.id, + run: () => this._commandService.executeCommand(command.id, ...(command.arguments || [])) + }); + } + for (const [_, group] of menu.getActions()) { for (const action of group) { if (action instanceof MenuItemAction) { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts index fb8393f665a..bccd1734dbf 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts @@ -15,7 +15,7 @@ import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; -import { InlineCompletion, InlineCompletionContext, InlineCompletions, InlineCompletionsProvider, InlineCompletionTriggerKind } from 'vs/editor/common/languages'; +import { Command, InlineCompletion, InlineCompletionContext, InlineCompletions, InlineCompletionsProvider, InlineCompletionTriggerKind } from 'vs/editor/common/languages'; import { BaseGhostTextWidgetModel, GhostText, GhostTextReplacement, GhostTextWidgetModel } from 'vs/editor/contrib/inlineCompletions/browser/ghostText'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { inlineSuggestCommitId } from 'vs/editor/contrib/inlineCompletions/browser/consts'; @@ -543,6 +543,11 @@ export class InlineCompletionsSession extends BaseGhostTextWidgetModel { this.onDidChangeEmitter.fire(); } + + public get commands(): Command[] { + const lists = new Set(this.cache.value?.completions.map(c => c.inlineCompletion.sourceInlineCompletions) || []); + return [...lists].flatMap(l => l.commands || []); + } } export class UpdateOperation implements IDisposable { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index c3dab93ceb4..57527a3afa3 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -6332,6 +6332,10 @@ declare namespace monaco.languages { export interface InlineCompletions { readonly items: readonly TItem[]; + /** + * A list of commands associated with the inline completions of this list. + */ + readonly commands?: Command[]; } export interface InlineCompletionsProvider { diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 2cc57319c69..c53758c8946 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1030,6 +1030,10 @@ class InlineCompletionAdapterBase { public async provideInlineCompletions(resource: URI, position: IPosition, context: languages.InlineCompletionContext, token: CancellationToken): Promise { return undefined; } + + public disposeCompletions(pid: number): void { } + + public handleDidShowCompletionItem(pid: number, idx: number): void { } } class InlineCompletionAdapter extends InlineCompletionAdapterBase { @@ -1104,7 +1108,7 @@ class InlineCompletionAdapter extends InlineCompletionAdapterBase { }; } - public disposeCompletions(pid: number) { + public override disposeCompletions(pid: number) { this._cache.delete(pid); const d = this._disposables.get(pid); if (d) { @@ -1113,7 +1117,7 @@ class InlineCompletionAdapter extends InlineCompletionAdapterBase { this._disposables.delete(pid); } - public handleDidShowCompletionItem(pid: number, idx: number): void { + public override handleDidShowCompletionItem(pid: number, idx: number): void { const completionItem = this._cache.get(pid, idx); if (completionItem) { InlineCompletionController.get(this._provider).fireOnDidShowCompletionItem({ @@ -1124,8 +1128,10 @@ class InlineCompletionAdapter extends InlineCompletionAdapterBase { } class InlineCompletionAdapterNew extends InlineCompletionAdapterBase { - private readonly _cache = new Cache('InlineCompletionItemNew'); - private readonly _disposables = new Map(); + private readonly _references = new ReferenceMap<{ + dispose(): void; + items: readonly vscode.InlineCompletionItemNew[]; + }>(); private readonly isAdditionProposedApiEnabled = isProposedApiEnabled(this.extension, 'inlineCompletionsAdditions'); @@ -1170,9 +1176,17 @@ class InlineCompletionAdapterNew extends InlineCompletionAdapterBase { } const normalizedResult = isArray(result) ? result : result.items; + const commands = isArray(result) ? [] : result.commands || []; - const pid = this._cache.add(normalizedResult); let disposableStore: DisposableStore | undefined = undefined; + const pid = this._references.createReferenceId({ + dispose() { + if (disposableStore) { + disposableStore.dispose(); + } + }, + items: normalizedResult + }); return { pid, @@ -1181,7 +1195,6 @@ class InlineCompletionAdapterNew extends InlineCompletionAdapterBase { if (item.command) { if (!disposableStore) { disposableStore = new DisposableStore(); - this._disposables.set(pid, disposableStore); } command = this._commands.toInternal(item.command, disposableStore); } @@ -1196,28 +1209,51 @@ class InlineCompletionAdapterNew extends InlineCompletionAdapterBase { completeBracketPairs: this.isAdditionProposedApiEnabled ? item.completeBracketPairs : false }); }), + commands: commands.map(c => { + if (!disposableStore) { + disposableStore = new DisposableStore(); + } + return this._commands.toInternal(c, disposableStore); + }) }; } - public disposeCompletions(pid: number) { - this._cache.delete(pid); - const d = this._disposables.get(pid); - if (d) { - d.clear(); - } - this._disposables.delete(pid); + public override disposeCompletions(pid: number) { + const data = this._references.disposeReferenceId(pid); + data?.dispose(); } - public handleDidShowCompletionItem(pid: number, idx: number): void { - const completionItem = this._cache.get(pid, idx); + public override handleDidShowCompletionItem(pid: number, idx: number): void { + const completionItem = this._references.get(pid)?.items[idx]; if (completionItem) { - if (this._provider.handleDidShowCompletionItem && isProposedApiEnabled(this.extension, 'inlineCompletionsAdditions')) { + if (this._provider.handleDidShowCompletionItem && this.isAdditionProposedApiEnabled) { this._provider.handleDidShowCompletionItem(completionItem); } } } } +class ReferenceMap { + private readonly _references = new Map(); + private _idPool = 1; + + createReferenceId(value: T): number { + const id = this._idPool++; + this._references.set(id, value); + return id; + } + + disposeReferenceId(referenceId: number): T | undefined { + const value = this._references.get(referenceId); + this._references.delete(referenceId); + return value; + } + + get(referenceId: number): T | undefined { + return this._references.get(referenceId); + } +} + export class InlineCompletionController implements vscode.InlineCompletionController { private static readonly map = new WeakMap, InlineCompletionController>(); @@ -2194,13 +2230,13 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF } $handleInlineCompletionDidShow(handle: number, pid: number, idx: number): void { - this._withAdapter(handle, InlineCompletionAdapter, async adapter => { + this._withAdapter(handle, InlineCompletionAdapterBase, async adapter => { adapter.handleDidShowCompletionItem(pid, idx); }, undefined, undefined); } $freeInlineCompletionsList(handle: number, pid: number): void { - this._withAdapter(handle, InlineCompletionAdapter, async adapter => { adapter.disposeCompletions(pid); }, undefined, undefined); + this._withAdapter(handle, InlineCompletionAdapterBase, async adapter => { adapter.disposeCompletions(pid); }, undefined, undefined); } // --- parameter hints diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 8ed1d005b1a..a69e00f05ec 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1643,8 +1643,11 @@ export class InlineSuggestionNew implements vscode.InlineCompletionItemNew { export class InlineSuggestionsNew implements vscode.InlineCompletionListNew { items: vscode.InlineCompletionItemNew[]; - constructor(items: vscode.InlineCompletionItemNew[]) { + commands: vscode.Command[] | undefined; + + constructor(items: vscode.InlineCompletionItemNew[], commands?: vscode.Command[]) { this.items = items; + this.commands = commands; } } diff --git a/src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts b/src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts index 200b7ebda04..8ab631571c8 100644 --- a/src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts +++ b/src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts @@ -18,28 +18,35 @@ declare module 'vscode' { * not cause a failure of the whole operation. * * @param selector A selector that defines the documents this provider is applicable to. - * @param provider A inline completion provider. + * @param provider An inline completion provider. * @return A {@link Disposable} that unregisters this provider when being disposed. */ export function registerInlineCompletionItemProviderNew(selector: DocumentSelector, provider: InlineCompletionItemProviderNew): Disposable; } - // TODO@API doc + /** + * The inline completion item provider interface defines the contract between extensions and + * the inline completion feature. + * + * Providers are asked for completions either explicitly by a user gesture or implicitly when typing. + */ export interface InlineCompletionItemProviderNew { /** * Provides inline completion items for the given position and document. * If inline completions are enabled, this method will be called whenever the user stopped typing. * It will also be called when the user explicitly triggers inline completions or asks for the next or previous inline completion. - * Use `context.triggerKind` to distinguish between these scenarios. + * `context.triggerKind` can be used to distinguish between these scenarios. */ provideInlineCompletionItems(document: TextDocument, position: Position, context: InlineCompletionContextNew, token: CancellationToken): ProviderResult; } - // TODO@API doc + /** + * Provides information about the context in which an inline completion was requested. + */ export interface InlineCompletionContextNew { /** - * How the completion was triggered. + * Describes how the inline completion was triggered. */ readonly triggerKind: InlineCompletionTriggerKindNew; @@ -52,21 +59,27 @@ declare module 'vscode' { * the inline completion must also replace `.` and start with `.log`, for example `.log()`. * * Inline completion providers are requested again whenever the selected item changes. - * - * The user must configure `"editor.suggest.preview": true` for this feature. */ readonly selectedCompletionInfo: SelectedCompletionInfoNew | undefined; } - // TODO@API find a better name, xyzFilter, xyzConstraint - // TODO@API doc + /** + * Describes the currently selected completion item. + */ export interface SelectedCompletionInfoNew { + /** + * The range that will be replaced if this completion item is accepted. + */ range: Range; + + /** + * The text the range will be replaced with if this completion is accepted. + */ text: string; } /** - * How an {@link InlineCompletionItemProvider inline completion provider} was triggered. + * Describes how an {@link InlineCompletionItemProvider inline completion provider} was triggered. */ export enum InlineCompletionTriggerKindNew { /** @@ -82,22 +95,29 @@ declare module 'vscode' { Automatic = 1, } - // TODO@API doc + /** + * Represents a collection of {@link InlineCompletionItemNew inline completion items} to be presented + * in the editor. + */ export class InlineCompletionListNew { + /** + * The inline completion items. + */ items: InlineCompletionItemNew[]; - // TODO@API We could keep this and allow for `vscode.Command` instances that explain - // the result. That would replace the existing proposed menu-identifier and be more LSP friendly - // TODO@API maybe use MarkdownString - // commands?: Command[]; // "Show More..." - // description: MarkdownString - /** - * @deprecated Return an array of Inline Completion items directly. Will be removed eventually. - */ - constructor(items: InlineCompletionItemNew[]); + * A list of commands associated with the inline completions of this list. + */ + commands?: Command[]; + + constructor(items: InlineCompletionItemNew[], commands?: Command[]); } + /** + * An inline completion item represents a text snippet that is proposed inline to complete text that is being typed. + * + * @see {@link InlineCompletionItemProviderNew.provideInlineCompletionItems} + */ export class InlineCompletionItemNew { /** * The text to replace the range with. Must be set. @@ -113,7 +133,7 @@ declare module 'vscode' { /** * A text that is used to decide if this inline completion should be shown. - * An inline completion is shown if the text to replace is a subword of the filter text. + * An inline completion is shown if the text to replace is a prefix of the filter text. */ filterText?: string; @@ -133,7 +153,13 @@ declare module 'vscode' { */ command?: Command; - // TODO@API doc + /** + * Creates a new inline completion item. + * + * @param insertText The text to replace the range with. + * @param range The range to replace. If not set, the word at the requested position will be used. + * @param command An optional {@link Command} that is executed *after* inserting this completion. + */ constructor(insertText: string | SnippetString, range?: Range, command?: Command); } } From 7a9093a66c0d3c0bd9ca48c63572e3569ebedcd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Thu, 7 Apr 2022 15:25:49 +0200 Subject: [PATCH 014/245] win32 setup: disable app dir inheritance (#338) --- build/win32/code.iss | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/build/win32/code.iss b/build/win32/code.iss index 87367f14907..72869dcac4d 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -82,6 +82,13 @@ Name: "associatewithfiles"; Description: "{cm:AssociateWithFiles,{#NameShort}}"; Name: "addtopath"; Description: "{cm:AddToPath}"; GroupDescription: "{cm:Other}" Name: "runcode"; Description: "{cm:RunAfter,{#NameShort}}"; GroupDescription: "{cm:Other}"; Check: WizardSilent +[Dirs] +#if "user" == InstallTarget +Name: "{app}"; Permissions: system-full admins-full creatorowner-full authusers-readexec users-readexec; AfterInstall: DisableAppDirInheritance +#else +Name: "{app}"; Permissions: system-full admins-full authusers-readexec users-readexec; AfterInstall: DisableAppDirInheritance +#endif + [Files] Source: "*"; Excludes: "\CodeSignSummary*.md,\tools,\tools\*,\resources\app\product.json"; DestDir: "{code:GetDestDir}"; Flags: ignoreversion recursesubdirs createallsubdirs Source: "tools\*"; DestDir: "{app}\tools"; Flags: ignoreversion @@ -1480,3 +1487,10 @@ end; #ifdef Debug #expr SaveToFile(AddBackslash(SourcePath) + "code-processed.iss") #endif + +procedure DisableAppDirInheritance(); +var + ResultCode: Integer; +begin + Exec(ExpandConstant('{sys}\icacls.exe'), ExpandConstant('"{app}" /inheritancelevel:r'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode); +end; \ No newline at end of file From 0e5a8e4bc0cd7f1a1889547360a37311c7060384 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Thu, 7 Apr 2022 15:26:01 +0200 Subject: [PATCH 015/245] Git - Disable automatic repository scanning in the root of the HOMEDRIVE (#339) * Disable automatic repository scanning in the root of the HOMEDRIVE * Fix condition * Pull request feedback --- extensions/git/src/model.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index f0236dc3304..71ac937c6f8 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -105,6 +105,7 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR private _onDidRemoveRemoteSourcePublisher = new EventEmitter(); readonly onDidRemoveRemoteSourcePublisher = this._onDidRemoveRemoteSourcePublisher.event; + private showRepoOnHomeDriveRootWarning = true; private pushErrorHandlers = new Set(); private disposables: Disposable[] = []; @@ -334,6 +335,22 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR return; } + // On Window, opening a git repository from the root of the HOMEDRIVE poses a security risk. + // We will only a open git repository from the root of the HOMEDRIVE if the user explicitly + // opens the HOMEDRIVE as a folder. Only show the warning once during repository discovery. + if (process.platform === 'win32' && process.env.HOMEDRIVE && pathEquals(`${process.env.HOMEDRIVE}\\`, repositoryRoot)) { + const isRepoInWorkspaceFolders = (workspace.workspaceFolders ?? []).find(f => pathEquals(f.uri.fsPath, repositoryRoot))!!; + + if (!isRepoInWorkspaceFolders) { + if (this.showRepoOnHomeDriveRootWarning) { + window.showWarningMessage(localize('repoOnHomeDriveRootWarning', "Unable to automatically open the git repository at '{0}'. To open that git repository, open it directly as a folder in VS Code.", repositoryRoot)); + this.showRepoOnHomeDriveRootWarning = false; + } + + return; + } + } + const dotGit = await this.git.getRepositoryDotGit(repositoryRoot); const repository = new Repository(this.git.open(repositoryRoot, dotGit), this, this, this.globalState, this.outputChannel, this.telemetryReporter); From c2beae1c0ff11947b0c5f8d547376a9be3c9778c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Fri, 8 Apr 2022 18:42:55 +0200 Subject: [PATCH 016/245] depend solely on icacls.exe for setting permissions (#341) --- build/win32/code.iss | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/build/win32/code.iss b/build/win32/code.iss index 72869dcac4d..0f9f20fc9ba 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -83,11 +83,7 @@ Name: "addtopath"; Description: "{cm:AddToPath}"; GroupDescription: "{cm:Other}" Name: "runcode"; Description: "{cm:RunAfter,{#NameShort}}"; GroupDescription: "{cm:Other}"; Check: WizardSilent [Dirs] -#if "user" == InstallTarget -Name: "{app}"; Permissions: system-full admins-full creatorowner-full authusers-readexec users-readexec; AfterInstall: DisableAppDirInheritance -#else -Name: "{app}"; Permissions: system-full admins-full authusers-readexec users-readexec; AfterInstall: DisableAppDirInheritance -#endif +Name: "{app}"; AfterInstall: DisableAppDirInheritance [Files] Source: "*"; Excludes: "\CodeSignSummary*.md,\tools,\tools\*,\resources\app\product.json"; DestDir: "{code:GetDestDir}"; Flags: ignoreversion recursesubdirs createallsubdirs @@ -1488,9 +1484,18 @@ end; #expr SaveToFile(AddBackslash(SourcePath) + "code-processed.iss") #endif +// https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/icacls +// https://docs.microsoft.com/en-US/windows/security/identity-protection/access-control/security-identifiers procedure DisableAppDirInheritance(); var ResultCode: Integer; + Permissions: string; begin - Exec(ExpandConstant('{sys}\icacls.exe'), ExpandConstant('"{app}" /inheritancelevel:r'), '', SW_HIDE, ewWaitUntilTerminated, ResultCode); + Permissions := '/grant:r "*S-1-5-18:F" /grant:r "*S-1-5-32-544:F" /grant:r "*S-1-5-11:RX" /grant:r "*S-1-5-32-545:RX"'; + + #if "user" == InstallTarget + Permissions := Permissions + ' /grant:r "*S-1-3-0:F"'; + #endif + + Exec(ExpandConstant('{sys}\icacls.exe'), ExpandConstant('"{app}" /inheritancelevel:r ') + Permissions, '', SW_HIDE, ewWaitUntilTerminated, ResultCode); end; \ No newline at end of file From c20b68a62d198e73df34de5f95671eb63c8b5ff2 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Mon, 11 Apr 2022 03:30:00 -0400 Subject: [PATCH 017/245] Fix permissions for everything inside the installation folder (#342) * add ci to iacls * make sure files get affected too Co-authored-by: Joao Moreno --- build/win32/code.iss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/win32/code.iss b/build/win32/code.iss index 0f9f20fc9ba..e96ca4bde77 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -1491,10 +1491,10 @@ var ResultCode: Integer; Permissions: string; begin - Permissions := '/grant:r "*S-1-5-18:F" /grant:r "*S-1-5-32-544:F" /grant:r "*S-1-5-11:RX" /grant:r "*S-1-5-32-545:RX"'; + Permissions := '/grant:r "*S-1-5-18:(OI)(CI)F" /grant:r "*S-1-5-32-544:(OI)(CI)F" /grant:r "*S-1-5-11:(OI)(CI)RX" /grant:r "*S-1-5-32-545:(OI)(CI)RX"'; #if "user" == InstallTarget - Permissions := Permissions + ' /grant:r "*S-1-3-0:F"'; + Permissions := Permissions + ' /grant:r "*S-1-3-0:(OI)(CI)F"'; #endif Exec(ExpandConstant('{sys}\icacls.exe'), ExpandConstant('"{app}" /inheritancelevel:r ') + Permissions, '', SW_HIDE, ewWaitUntilTerminated, ResultCode); From 20bd040c95fb761b4aa13f67ea3cb70aac8cce63 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Tue, 12 Apr 2022 14:14:19 -0400 Subject: [PATCH 018/245] Merge internal options to timeline options (#147304) --- .../workbench/api/browser/mainThreadTimeline.ts | 6 +++--- src/vs/workbench/api/common/extHost.protocol.ts | 4 ++-- src/vs/workbench/api/common/extHostTimeline.ts | 16 ++++++++-------- .../localHistory/browser/localHistoryTimeline.ts | 4 ++-- .../contrib/timeline/browser/timelinePane.ts | 5 +++-- .../contrib/timeline/common/timeline.ts | 11 ++++------- .../contrib/timeline/common/timelineService.ts | 6 +++--- 7 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTimeline.ts b/src/vs/workbench/api/browser/mainThreadTimeline.ts index 6636909bbdf..d6547f29752 100644 --- a/src/vs/workbench/api/browser/mainThreadTimeline.ts +++ b/src/vs/workbench/api/browser/mainThreadTimeline.ts @@ -9,7 +9,7 @@ import { URI } from 'vs/base/common/uri'; import { ILogService } from 'vs/platform/log/common/log'; import { MainContext, MainThreadTimelineShape, ExtHostTimelineShape, ExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; -import { TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, ITimelineService, InternalTimelineOptions, Timeline } from 'vs/workbench/contrib/timeline/common/timeline'; +import { TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, ITimelineService, Timeline } from 'vs/workbench/contrib/timeline/common/timeline'; import { revive } from 'vs/base/common/marshalling'; @extHostNamedCustomer(MainContext.MainThreadTimeline) @@ -40,8 +40,8 @@ export class MainThreadTimeline implements MainThreadTimelineShape { this._timelineService.registerTimelineProvider({ ...provider, onDidChange: onDidChange.event, - async provideTimeline(uri: URI, options: TimelineOptions, token: CancellationToken, internalOptions?: InternalTimelineOptions) { - return revive(await proxy.$getTimeline(provider.id, uri, options, token, internalOptions)); + async provideTimeline(uri: URI, options: TimelineOptions, token: CancellationToken) { + return revive(await proxy.$getTimeline(provider.id, uri, options, token)); }, dispose() { emitters.delete(provider.id); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index b78a95454a6..e28db7d5e55 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -58,7 +58,7 @@ import { InputValidationType } from 'vs/workbench/contrib/scm/common/scm'; import { IWorkspaceSymbol } from 'vs/workbench/contrib/search/common/search'; import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { CoverageDetails, ExtensionRunTestsRequest, IFileCoverage, ISerializedTestResults, ITestItem, ITestMessage, ITestRunProfile, ITestRunTask, ResolvedTestRunRequest, RunTestForControllerRequest, TestResultState, TestsDiffOp } from 'vs/workbench/contrib/testing/common/testTypes'; -import { InternalTimelineOptions, Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor } from 'vs/workbench/contrib/timeline/common/timeline'; +import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor } from 'vs/workbench/contrib/timeline/common/timeline'; import { TypeHierarchyItem } from 'vs/workbench/contrib/typeHierarchy/common/typeHierarchy'; import { AuthenticationProviderInformation, AuthenticationSession, AuthenticationSessionsChangeEvent } from 'vs/workbench/services/authentication/common/authentication'; import { EditorGroupColumn } from 'vs/workbench/services/editor/common/editorGroupColumn'; @@ -2138,7 +2138,7 @@ export interface ExtHostTunnelServiceShape { } export interface ExtHostTimelineShape { - $getTimeline(source: string, uri: UriComponents, options: TimelineOptions, token: CancellationToken, internalOptions?: InternalTimelineOptions): Promise | undefined>; + $getTimeline(source: string, uri: UriComponents, options: TimelineOptions, token: CancellationToken): Promise | undefined>; } export const enum ExtHostTestingResource { diff --git a/src/vs/workbench/api/common/extHostTimeline.ts b/src/vs/workbench/api/common/extHostTimeline.ts index baab688ad31..c9d9e611649 100644 --- a/src/vs/workbench/api/common/extHostTimeline.ts +++ b/src/vs/workbench/api/common/extHostTimeline.ts @@ -7,7 +7,7 @@ import * as vscode from 'vscode'; import { UriComponents, URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ExtHostTimelineShape, MainThreadTimelineShape, IMainContext, MainContext } from 'vs/workbench/api/common/extHost.protocol'; -import { Timeline, TimelineItem, TimelineOptions, TimelineProvider, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline'; +import { Timeline, TimelineItem, TimelineOptions, TimelineProvider } from 'vs/workbench/contrib/timeline/common/timeline'; import { IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { CancellationToken } from 'vs/base/common/cancellation'; import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; @@ -19,7 +19,7 @@ import { isString } from 'vs/base/common/types'; export interface IExtHostTimeline extends ExtHostTimelineShape { readonly _serviceBrand: undefined; - $getTimeline(id: string, uri: UriComponents, options: vscode.TimelineOptions, token: vscode.CancellationToken, internalOptions?: InternalTimelineOptions): Promise; + $getTimeline(id: string, uri: UriComponents, options: vscode.TimelineOptions, token: vscode.CancellationToken): Promise; } export const IExtHostTimeline = createDecorator('IExtHostTimeline'); @@ -51,9 +51,9 @@ export class ExtHostTimeline implements IExtHostTimeline { }); } - async $getTimeline(id: string, uri: UriComponents, options: vscode.TimelineOptions, token: vscode.CancellationToken, internalOptions?: InternalTimelineOptions): Promise { + async $getTimeline(id: string, uri: UriComponents, options: vscode.TimelineOptions, token: vscode.CancellationToken): Promise { const provider = this._providers.get(id); - return provider?.provideTimeline(URI.revive(uri), options, token, internalOptions); + return provider?.provideTimeline(URI.revive(uri), options, token); } registerTimelineProvider(scheme: string | string[], provider: vscode.TimelineProvider, _extensionId: ExtensionIdentifier, commandConverter: CommandsConverter): IDisposable { @@ -71,8 +71,8 @@ export class ExtHostTimeline implements IExtHostTimeline { ...provider, scheme: scheme, onDidChange: undefined, - async provideTimeline(uri: URI, options: TimelineOptions, token: CancellationToken, internalOptions?: InternalTimelineOptions) { - if (internalOptions?.resetCache) { + async provideTimeline(uri: URI, options: TimelineOptions, token: CancellationToken) { + if (options?.resetCache) { timelineDisposables.clear(); // For now, only allow the caching of a single Uri @@ -87,7 +87,7 @@ export class ExtHostTimeline implements IExtHostTimeline { // TODO: Should we bother converting all the data if we aren't caching? Meaning it is being requested by an extension? - const convertItem = convertTimelineItem(uri, internalOptions); + const convertItem = convertTimelineItem(uri, options); return { ...result, source: provider.id, @@ -106,7 +106,7 @@ export class ExtHostTimeline implements IExtHostTimeline { } private convertTimelineItem(source: string, commandConverter: CommandsConverter, disposables: DisposableStore) { - return (uri: URI, options?: InternalTimelineOptions) => { + return (uri: URI, options?: TimelineOptions) => { let items: Map | undefined; if (options?.cacheResults) { let itemsByUri = this._itemsBySourceAndUriMap.get(source); diff --git a/src/vs/workbench/contrib/localHistory/browser/localHistoryTimeline.ts b/src/vs/workbench/contrib/localHistory/browser/localHistoryTimeline.ts index c0e31f90dd3..2dee3866cbd 100644 --- a/src/vs/workbench/contrib/localHistory/browser/localHistoryTimeline.ts +++ b/src/vs/workbench/contrib/localHistory/browser/localHistoryTimeline.ts @@ -8,7 +8,7 @@ import { Emitter } from 'vs/base/common/event'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { InternalTimelineOptions, ITimelineService, Timeline, TimelineChangeEvent, TimelineItem, TimelineOptions, TimelineProvider } from 'vs/workbench/contrib/timeline/common/timeline'; +import { ITimelineService, Timeline, TimelineChangeEvent, TimelineItem, TimelineOptions, TimelineProvider } from 'vs/workbench/contrib/timeline/common/timeline'; import { IWorkingCopyHistoryEntry, IWorkingCopyHistoryService } from 'vs/workbench/services/workingCopy/common/workingCopyHistory'; import { URI } from 'vs/base/common/uri'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; @@ -102,7 +102,7 @@ export class LocalHistoryTimeline extends Disposable implements IWorkbenchContri }); } - async provideTimeline(uri: URI, options: TimelineOptions, token: CancellationToken, internalOptions?: InternalTimelineOptions): Promise { + async provideTimeline(uri: URI, options: TimelineOptions, token: CancellationToken): Promise { const items: TimelineItem[] = []; // Try to convert the provided `uri` into a form that is likely diff --git a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts index 10c04d75a9c..6c2233986e1 100644 --- a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts +++ b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts @@ -571,9 +571,10 @@ export class TimelinePane extends ViewPane { } } request?.tokenSource.dispose(true); - + options.cacheResults = true; + options.resetCache = reset; request = this.timelineService.getTimeline( - source, uri, options, new CancellationTokenSource(), { cacheResults: true, resetCache: reset } + source, uri, options, new CancellationTokenSource() ); if (request === undefined) { diff --git a/src/vs/workbench/contrib/timeline/common/timeline.ts b/src/vs/workbench/contrib/timeline/common/timeline.ts index 8b9f870c14d..5185f65703e 100644 --- a/src/vs/workbench/contrib/timeline/common/timeline.ts +++ b/src/vs/workbench/contrib/timeline/common/timeline.ts @@ -77,11 +77,8 @@ export interface TimelineChangeEvent { export interface TimelineOptions { cursor?: string; limit?: number | { timestamp: number; id?: string }; -} - -export interface InternalTimelineOptions { - cacheResults: boolean; - resetCache: boolean; + resetCache?: boolean; + cacheResults?: boolean; } export interface Timeline { @@ -101,7 +98,7 @@ export interface Timeline { export interface TimelineProvider extends TimelineProviderDescriptor, IDisposable { onDidChange?: Event; - provideTimeline(uri: URI, options: TimelineOptions, token: CancellationToken, internalOptions?: InternalTimelineOptions): Promise; + provideTimeline(uri: URI, options: TimelineOptions, token: CancellationToken): Promise; } export interface TimelineSource { @@ -152,7 +149,7 @@ export interface ITimelineService { getSources(): TimelineSource[]; - getTimeline(id: string, uri: URI, options: TimelineOptions, tokenSource: CancellationTokenSource, internalOptions?: InternalTimelineOptions): TimelineRequest | undefined; + getTimeline(id: string, uri: URI, options: TimelineOptions, tokenSource: CancellationTokenSource): TimelineRequest | undefined; setUri(uri: URI): void; } diff --git a/src/vs/workbench/contrib/timeline/common/timelineService.ts b/src/vs/workbench/contrib/timeline/common/timelineService.ts index 52bc586f90d..190dfa2501c 100644 --- a/src/vs/workbench/contrib/timeline/common/timelineService.ts +++ b/src/vs/workbench/contrib/timeline/common/timelineService.ts @@ -8,7 +8,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { ILogService } from 'vs/platform/log/common/log'; -import { ITimelineService, TimelineChangeEvent, TimelineOptions, TimelineProvidersChangeEvent, TimelineProvider, InternalTimelineOptions, TimelinePaneId } from './timeline'; +import { ITimelineService, TimelineChangeEvent, TimelineOptions, TimelineProvidersChangeEvent, TimelineProvider, TimelinePaneId } from './timeline'; import { IViewsService } from 'vs/workbench/common/views'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; @@ -44,7 +44,7 @@ export class TimelineService implements ITimelineService { return [...this.providers.values()].map(p => ({ id: p.id, label: p.label })); } - getTimeline(id: string, uri: URI, options: TimelineOptions, tokenSource: CancellationTokenSource, internalOptions?: InternalTimelineOptions) { + getTimeline(id: string, uri: URI, options: TimelineOptions, tokenSource: CancellationTokenSource) { this.logService.trace(`TimelineService#getTimeline(${id}): uri=${uri.toString()}`); const provider = this.providers.get(id); @@ -61,7 +61,7 @@ export class TimelineService implements ITimelineService { } return { - result: provider.provideTimeline(uri, options, tokenSource.token, internalOptions) + result: provider.provideTimeline(uri, options, tokenSource.token) .then(result => { if (result === undefined) { return undefined; From 27dd7b41b6a830d5ea8eb896d5b2bb9c4bcd95ee Mon Sep 17 00:00:00 2001 From: Dirk Baeumer Date: Tue, 12 Apr 2022 20:20:05 +0200 Subject: [PATCH 019/245] Remove unused import --- .../workbench/services/preferences/browser/preferencesService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index 8b29a6f2650..555a3a7ee20 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -17,7 +17,6 @@ import { IModelService } from 'vs/editor/common/services/model'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import * as nls from 'vs/nls'; -import { ICommandService } from 'vs/platform/commands/common/commands'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Extensions, getDefaultValue, IConfigurationRegistry, OVERRIDE_PROPERTY_REGEX } from 'vs/platform/configuration/common/configurationRegistry'; import { EditorResolution } from 'vs/platform/editor/common/editor'; From 527d63162ad6b43a5e106e93ed05c3a299bda947 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Tue, 12 Apr 2022 17:18:13 -0700 Subject: [PATCH 020/245] address feedback by having concrete ValidationMessage type --- .../workbench/api/common/extHost.api.impl.ts | 1 - .../workbench/api/common/extHostQuickOpen.ts | 48 ++++++++++++++----- .../vscode.proposed.inputBoxSeverity.d.ts | 12 +++-- 3 files changed, 44 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index acdf0672025..42f84709fbe 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -625,7 +625,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I showInputBox(options?: vscode.InputBoxOptions, token?: vscode.CancellationToken) { if (options?.validateInput2) { checkProposedApiEnabled(extension, 'inputBoxSeverity'); - options.validateInput = options.validateInput2 as any; } return extHostQuickOpen.showInput(options, token); }, diff --git a/src/vs/workbench/api/common/extHostQuickOpen.ts b/src/vs/workbench/api/common/extHostQuickOpen.ts index 0df03b79137..a3c48d6cc54 100644 --- a/src/vs/workbench/api/common/extHostQuickOpen.ts +++ b/src/vs/workbench/api/common/extHostQuickOpen.ts @@ -3,16 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { asPromise } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter } from 'vs/base/common/event'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { IExtHostWorkspaceProvider } from 'vs/workbench/api/common/extHostWorkspace'; -import type { InputBox, InputBoxOptions, InputBoxValidationSeverity, QuickInput, QuickInputButton, QuickPick, QuickPickItem, QuickPickItemButtonEvent, QuickPickOptions, WorkspaceFolder, WorkspaceFolderPickOptions } from 'vscode'; +import { InputBox, InputBoxOptions, InputBoxValidationMessage, QuickInput, QuickInputButton, QuickPick, QuickPickItem, QuickPickItemButtonEvent, QuickPickOptions, WorkspaceFolder, WorkspaceFolderPickOptions } from 'vscode'; import { ExtHostQuickOpenShape, IMainContext, MainContext, TransferQuickInput, TransferQuickInputButton, TransferQuickPickItemOrSeparator } from './extHost.protocol'; import { URI } from 'vs/base/common/uri'; -import { ThemeIcon, QuickInputButtons, QuickPickItemKind } from 'vs/workbench/api/common/extHostTypes'; +import { ThemeIcon, QuickInputButtons, QuickPickItemKind, InputBoxValidationSeverity } from 'vs/workbench/api/common/extHostTypes'; import { isCancellationError } from 'vs/base/common/errors'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { coalesce } from 'vs/base/common/arrays'; @@ -46,7 +45,7 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx private _commands: ExtHostCommands; private _onDidSelectItem?: (handle: number) => void; - private _validateInput?: (input: string) => string | { content: string; severity: Severity } | undefined | null | Thenable; + private _validateInput?: (input: string) => string | InputBoxValidationMessage | undefined | null | Thenable; private _sessions = new Map(); @@ -148,7 +147,7 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx showInput(options?: InputBoxOptions, token: CancellationToken = CancellationToken.None): Promise { // global validate fn used in callback below - this._validateInput = options ? options.validateInput : undefined; + this._validateInput = options ? options.validateInput2 ?? options.validateInput : undefined; return proxy.$input(options, typeof this._validateInput === 'function', token) .then(undefined, err => { @@ -160,11 +159,36 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx }); } - $validateInput(input: string): Promise { - if (this._validateInput) { - return asPromise(() => this._validateInput!(input)); + async $validateInput(input: string): Promise { + if (!this._validateInput) { + return; } - return Promise.resolve(undefined); + + const result = await this._validateInput(input); + if (!result || typeof result === 'string') { + return result; + } + + let severity: Severity; + switch (result.severity) { + case InputBoxValidationSeverity.Info: + severity = Severity.Info; + break; + case InputBoxValidationSeverity.Warning: + severity = Severity.Warning; + break; + case InputBoxValidationSeverity.Error: + severity = Severity.Error; + break; + default: + severity = result.message ? Severity.Error : Severity.Ignore; + break; + } + + return { + content: result.message, + severity + }; } // ---- workspace folder picker @@ -675,7 +699,7 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx private _password = false; private _prompt: string | undefined; private _validationMessage: string | undefined; - private _validationMessage2: string | { content: string; severity: InputBoxValidationSeverity } | undefined; + private _validationMessage2: string | InputBoxValidationMessage | undefined; constructor(private readonly extension: IExtensionDescription, onDispose: () => void) { super(extension.identifier, onDispose); @@ -713,7 +737,7 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx return this._validationMessage2; } - set validationMessage2(validationMessage: string | { content: string; severity: InputBoxValidationSeverity } | undefined) { + set validationMessage2(validationMessage: string | InputBoxValidationMessage | undefined) { checkProposedApiEnabled(this.extension, 'inputBoxSeverity'); this._validationMessage2 = validationMessage; if (!validationMessage) { @@ -721,7 +745,7 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx } else if (typeof validationMessage === 'string') { this.update({ validationMessage, severity: Severity.Error }); } else { - this.update({ validationMessage: validationMessage.content, severity: validationMessage.severity ?? Severity.Error }); + this.update({ validationMessage: validationMessage.message, severity: validationMessage.severity ?? Severity.Error }); } } } diff --git a/src/vscode-dts/vscode.proposed.inputBoxSeverity.d.ts b/src/vscode-dts/vscode.proposed.inputBoxSeverity.d.ts index ceb932ecdc6..292abaa8f86 100644 --- a/src/vscode-dts/vscode.proposed.inputBoxSeverity.d.ts +++ b/src/vscode-dts/vscode.proposed.inputBoxSeverity.d.ts @@ -13,19 +13,23 @@ declare module 'vscode' { Error = 3 } + export interface InputBoxValidationMessage { + readonly message: string; + readonly severity: InputBoxValidationSeverity; + } + export interface InputBoxOptions { /** * The validation message to display. This will become the new {@link InputBoxOptions#validateInput} upon finalization. */ - // TODO@API consider to extract InputBoxValidationMessage - validateInput2?(value: string): string | { content: string; severity: InputBoxValidationSeverity } | undefined | null | - Thenable; + validateInput2?(value: string): string | InputBoxValidationMessage | undefined | null | + Thenable; } export interface InputBox { /** * The validation message to display. This will become the new {@link InputBox#validationMessage} upon finalization. */ - validationMessage2: string | { content: string; severity: InputBoxValidationSeverity } | undefined; + validationMessage2: string | InputBoxValidationMessage | undefined; } } From fbf7708053bb1b96f8d9ed048bdeeae8505c4ec3 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 12 Apr 2022 17:17:36 -0700 Subject: [PATCH 021/245] Block pointer events on webviews when dragging from desktop into VS Code Fixes #146967 When dragging a file from desktop over a webview, by default the iframe of the webview will eat all of the dnd events To fix this, we handle the events inside the webview and tell the outer renderer to start handling them. We do this by disabling pointer events on the iframe, which causes Chrome to start dispatching future dnd events back to the iframe container's element (this also works around https://bugs.chromium.org/p/chromium/issues/detail?id=923651) For now I've tried to limit this change to cases where files are being dragged from desktop (drags within VS Code should already be handled) --- .../contrib/webview/browser/pre/main.js | 18 +++++++++ .../contrib/webview/browser/webviewElement.ts | 40 +++++++++++++++---- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index 48beccb020a..8c91fdae9c9 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -1021,6 +1021,24 @@ onDomReady(() => { }); }); + const dragHandler = (/** @type {DragEvent} */ e) => { + if (e.defaultPrevented) { + // Extension code has already handled this event + return; + } + + if (!e.dataTransfer) { + return; + } + + // Only handle drags from outside editor for now + if (e.target === contentWindow.document.documentElement) { + hostMessaging.postMessage('drag-start'); + } + }; + contentWindow.addEventListener('dragenter', dragHandler); + contentWindow.addEventListener('dragover', dragHandler); + unloadMonitor.onIframeLoaded(newFrame); } }); diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index c790753eb0b..6cd87f63f28 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { isFirefox } from 'vs/base/browser/browser'; -import { addDisposableListener } from 'vs/base/browser/dom'; +import { addDisposableListener, EventType } from 'vs/base/browser/dom'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { IAction } from 'vs/base/common/actions'; import { ThrottledDelayer } from 'vs/base/common/async'; @@ -59,6 +59,7 @@ export const enum WebviewMessageChannels { didKeydown = 'did-keydown', didKeyup = 'did-keyup', didContextMenu = 'did-context-menu', + dragStart = 'drag-start', } interface IKeydownEvent { @@ -350,6 +351,10 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD } })); + this._register(this.on(WebviewMessageChannels.dragStart, () => { + this.startBlockingIframeDragEvents(); + })); + if (options.enableFindWidget) { this._webviewFindWidget = this._register(instantiationService.createInstance(WebviewFindWidget, this)); this.styledFindWidget(); @@ -489,9 +494,32 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD if (this._webviewFindWidget) { parent.appendChild(this._webviewFindWidget.getDomNode()); } + + [EventType.MOUSE_DOWN, EventType.MOUSE_MOVE, EventType.DROP].forEach(eventName => { + this._register(addDisposableListener(parent, eventName, () => { + this.stopBlockingIframeDragEvents(); + })); + }); + + [parent, window].forEach(node => this._register(addDisposableListener(node as HTMLElement, EventType.DRAG_END, () => { + this.stopBlockingIframeDragEvents(); + }))); + parent.appendChild(this.element); } + private startBlockingIframeDragEvents() { + if (this.element) { + this.element.style.pointerEvents = 'none'; + } + } + + private stopBlockingIframeDragEvents() { + if (this.element) { + this.element.style.pointerEvents = 'auto'; + } + } + protected webviewContentEndpoint(encodedWebviewOrigin: string): string { const endpoint = this._environmentService.webviewExternalEndpoint!.replace('{{uuid}}', encodedWebviewOrigin); if (endpoint[endpoint.length - 1] === '/') { @@ -685,18 +713,14 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD } windowDidDragStart(): void { - // Webview break drag and droping around the main window (no events are generated when you are over them) + // Webview break drag and dropping around the main window (no events are generated when you are over them) // Work around this by disabling pointer events during the drag. // https://github.com/electron/electron/issues/18226 - if (this.element) { - this.element.style.pointerEvents = 'none'; - } + this.startBlockingIframeDragEvents(); } windowDidDragEnd(): void { - if (this.element) { - this.element.style.pointerEvents = ''; - } + this.stopBlockingIframeDragEvents(); } public selectAll() { From a2e45088bcd47325603a52baa24fd8c8b65e0205 Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 12 Apr 2022 17:24:15 -0700 Subject: [PATCH 022/245] controler id --- .../workbench/api/common/extHostNotebookKernels.ts | 12 +++++++++--- .../vscode.proposed.notebookProxyController.d.ts | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/api/common/extHostNotebookKernels.ts b/src/vs/workbench/api/common/extHostNotebookKernels.ts index 57486a80e0d..b3d560db5f5 100644 --- a/src/vs/workbench/api/common/extHostNotebookKernels.ts +++ b/src/vs/workbench/api/common/extHostNotebookKernels.ts @@ -265,7 +265,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { return controller; } - createNotebookProxyController(extension: IExtensionDescription, id: string, viewType: string, label: string, handler: () => vscode.NotebookController | Thenable): vscode.NotebookProxyController { + createNotebookProxyController(extension: IExtensionDescription, id: string, viewType: string, label: string, handler: () => vscode.NotebookController | string | Thenable): vscode.NotebookProxyController { const handle = this._handlePool++; let isDisposed = false; @@ -461,8 +461,14 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { const controller = await obj.controller.resolveHandler(); let matchedKernelData: IKernelData | undefined; this._kernelData.forEach(d => { - if (d.controller.id === controller.id) { - matchedKernelData = d; + if (typeof controller === 'string') { + if (d.controller.id === controller) { + matchedKernelData = d; + } + } else { + if (d.controller.id === controller.id) { + matchedKernelData = d; + } } }); diff --git a/src/vscode-dts/vscode.proposed.notebookProxyController.d.ts b/src/vscode-dts/vscode.proposed.notebookProxyController.d.ts index 5d873192c4a..07f8e833f15 100644 --- a/src/vscode-dts/vscode.proposed.notebookProxyController.d.ts +++ b/src/vscode-dts/vscode.proposed.notebookProxyController.d.ts @@ -40,7 +40,7 @@ declare module 'vscode' { */ kind?: string; - resolveHandler: () => NotebookController | Thenable; + resolveHandler: () => NotebookController | string | Thenable; readonly onDidChangeSelectedNotebooks: Event<{ readonly notebook: NotebookDocument; readonly selected: boolean }>; @@ -51,6 +51,6 @@ declare module 'vscode' { } export namespace notebooks { - export function createNotebookProxyController(id: string, notebookType: string, label: string, resolveHandler: () => NotebookController | Thenable): NotebookProxyController; + export function createNotebookProxyController(id: string, notebookType: string, label: string, resolveHandler: () => NotebookController | string | Thenable): NotebookProxyController; } } From 214ff3b7eb534732a041f83c8860bbcfd13bd0c3 Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Tue, 12 Apr 2022 19:49:10 -0700 Subject: [PATCH 023/245] Add settings editor search funnel button (#147260) Ref #145710 --- .../browser/media/settingsEditor2.css | 4 +- .../preferences/browser/preferencesIcons.ts | 1 + .../preferences/browser/settingsEditor2.ts | 16 ++- .../preferences/browser/settingsSearchMenu.ts | 125 ++++++++++++++++++ .../contrib/preferences/common/preferences.ts | 2 + 5 files changed, 141 insertions(+), 7 deletions(-) create mode 100644 src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css index 7145fe94aa9..a09f097b305 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css @@ -39,7 +39,7 @@ .settings-editor > .settings-header > .search-container > .settings-count-widget { position: absolute; - right: 35px; + right: 46px; top: 0px; margin: 4px 0px; } @@ -55,7 +55,7 @@ top: 0; right: 0; height: 100%; - width: 30px; + width: 43px; } .settings-editor > .settings-header > .search-container > .settings-clear-widget .action-label { diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesIcons.ts b/src/vs/workbench/contrib/preferences/browser/preferencesIcons.ts index 6692bf47bd4..0c31b455aaf 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesIcons.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesIcons.ts @@ -25,4 +25,5 @@ export const settingsRemoveIcon = registerIcon('settings-remove', Codicon.close, export const settingsDiscardIcon = registerIcon('settings-discard', Codicon.discard, localize('preferencesDiscardIcon', 'Icon for the discard action in the Settings UI.')); export const preferencesClearInputIcon = registerIcon('preferences-clear-input', Codicon.clearAll, localize('preferencesClearInput', 'Icon for clear input in the Settings and keybinding UI.')); +export const preferencesFilterIcon = registerIcon('preferences-filter', Codicon.filter, localize('settingsFilter', 'Icon for the button that suggests filters for the Settings UI.')); export const preferencesOpenSettingsIcon = registerIcon('preferences-open-settings', Codicon.goToFile, localize('preferencesOpenSettings', 'Icon for open settings commands.')); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 780ebf0a742..392abf569fe 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -43,14 +43,14 @@ import { commonlyUsedData, tocData } from 'vs/workbench/contrib/preferences/brow import { AbstractSettingRenderer, HeightChangeParams, ISettingLinkClickEvent, ISettingOverrideClickEvent, resolveConfiguredUntrustedSettings, createTocTreeForExtensionSettings, resolveSettingsTree, SettingsTree, SettingTreeRenderers } from 'vs/workbench/contrib/preferences/browser/settingsTree'; import { ISettingsEditorViewState, parseQuery, SearchResultIdx, SearchResultModel, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeModel, SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; import { createTOCIterator, TOCTree, TOCTreeModel } from 'vs/workbench/contrib/preferences/browser/tocTree'; -import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, ENABLE_LANGUAGE_FILTER, EXTENSION_SETTING_TAG, FEATURE_SETTING_TAG, ID_SETTING_TAG, IPreferencesSearchService, ISearchProvider, LANGUAGE_SETTING_TAG, MODIFIED_SETTING_TAG, REQUIRE_TRUSTED_WORKSPACE_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, WORKSPACE_TRUST_SETTING_TAG } from 'vs/workbench/contrib/preferences/common/preferences'; +import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, ENABLE_LANGUAGE_FILTER, EXTENSION_SETTING_TAG, FEATURE_SETTING_TAG, ID_SETTING_TAG, IPreferencesSearchService, ISearchProvider, LANGUAGE_SETTING_TAG, MODIFIED_SETTING_TAG, REQUIRE_TRUSTED_WORKSPACE_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SUGGEST_FILTERS, WORKSPACE_TRUST_SETTING_TAG } from 'vs/workbench/contrib/preferences/common/preferences'; import { settingsHeaderBorder, settingsSashBorder, settingsTextInputBorder } from 'vs/workbench/contrib/preferences/common/settingsEditorColorRegistry'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IOpenSettingsOptions, IPreferencesService, ISearchResult, ISettingsEditorModel, ISettingsEditorOptions, SettingMatchType, SettingValueType, validateSettingsEditorOptions } from 'vs/workbench/services/preferences/common/preferences'; import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; import { Settings2EditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; import { IUserDataSyncWorkbenchService } from 'vs/workbench/services/userDataSync/common/userDataSync'; -import { preferencesClearInputIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons'; +import { preferencesClearInputIcon, preferencesFilterIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons'; import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; import { IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration'; @@ -58,6 +58,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { Orientation, Sizing, SplitView } from 'vs/base/browser/ui/splitview/splitview'; import { Color } from 'vs/base/common/color'; import { ILanguageService } from 'vs/editor/common/languages/language'; +import { SettingsSearchFilterDropdownMenuActionViewItem } from 'vs/workbench/contrib/preferences/browser/settingsSearchMenu'; export const enum SettingsFocusContext { Search, @@ -525,7 +526,7 @@ export class SettingsEditor2 extends EditorPane { const searchContainer = DOM.append(this.headerContainer, $('.search-container')); const clearInputAction = new Action(SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, localize('clearInput', "Clear Settings Search Input"), ThemeIcon.asClassName(preferencesClearInputIcon), false, async () => this.clearSearchResults()); - + const filterAction = new Action(SETTINGS_EDITOR_COMMAND_SUGGEST_FILTERS, localize('filterInput', "Filter Settings"), ThemeIcon.asClassName(preferencesFilterIcon)); this.searchWidget = this._register(this.instantiationService.createInstance(SuggestEnabledInput, `${SettingsEditor2.ID}.searchbox`, searchContainer, { triggerCharacters: ['@', ':'], provideResults: (query: string) => { @@ -603,10 +604,15 @@ export class SettingsEditor2 extends EditorPane { const actionBar = this._register(new ActionBar(this.controlsElement, { animated: false, - actionViewItemProvider: (_action) => { return undefined; } + actionViewItemProvider: (_action) => { + if (_action.id === filterAction.id) { + return this.instantiationService.createInstance(SettingsSearchFilterDropdownMenuActionViewItem, _action, this.actionRunner, this.searchWidget); + } + return undefined; + } })); - actionBar.push([clearInputAction], { label: false, icon: true }); + actionBar.push([clearInputAction, filterAction], { label: false, icon: true }); } private onDidSettingsTargetChange(target: SettingsTarget): void { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts b/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts new file mode 100644 index 00000000000..d739a8752f5 --- /dev/null +++ b/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts @@ -0,0 +1,125 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; +import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; +import { IAction, IActionRunner } from 'vs/base/common/actions'; +import { localize } from 'vs/nls'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { SuggestEnabledInput } from 'vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput'; +import { EXTENSION_SETTING_TAG, FEATURE_SETTING_TAG, GENERAL_TAG_SETTING_TAG, ID_SETTING_TAG, LANGUAGE_SETTING_TAG, MODIFIED_SETTING_TAG } from 'vs/workbench/contrib/preferences/common/preferences'; + +export class SettingsSearchFilterDropdownMenuActionViewItem extends DropdownMenuActionViewItem { + constructor( + action: IAction, + actionRunner: IActionRunner | undefined, + private readonly searchWidget: SuggestEnabledInput, + @IContextMenuService contextMenuService: IContextMenuService, + @ICommandService private readonly commandService: ICommandService + ) { + super(action, + { getActions: () => this.getActions() }, + contextMenuService, + { + actionRunner, + classNames: action.class, + anchorAlignmentProvider: () => AnchorAlignment.RIGHT, + menuAsChild: true + } + ); + } + + override render(container: HTMLElement): void { + super.render(container); + } + + private doSearchWidgetAction(queryToAppend: string, triggerSuggest: boolean) { + this.searchWidget.setValue(this.searchWidget.getValue().trimEnd() + ' ' + queryToAppend); + this.searchWidget.focus(); + if (triggerSuggest) { + this.commandService.executeCommand('editor.action.triggerSuggest'); + } + } + + private createAction(id: string, label: string, tooltip: string, queryToAppend: string, triggerSuggest: boolean): IAction { + return { + id, + label, + tooltip, + class: undefined, + enabled: true, + checked: false, + run: () => { this.doSearchWidgetAction(queryToAppend, triggerSuggest); }, + dispose: () => { } + }; + } + + private createModifiedAction(): IAction { + // The modified action works slightly differently than the other actions. + // It is more like a checkbox on/off toggle. + const queryContainsModifiedTag = this.searchWidget.getValue().split(' ').some(word => word === `@${MODIFIED_SETTING_TAG}`); + return { + id: 'modifiedSettingsSearch', + label: localize('modifiedSettingsSearch', "Modified"), + tooltip: localize('modifiedSettingsSearchTooltip', "View modified settings only"), + class: undefined, + enabled: true, + checked: queryContainsModifiedTag, + run: () => { + // Append the tag, otherwise remove it from the query. + if (!queryContainsModifiedTag) { + this.searchWidget.setValue(this.searchWidget.getValue().trimEnd() + ` @${MODIFIED_SETTING_TAG}`); + } else { + const queryWithoutModifiedTag = this.searchWidget.getValue().split(' ').filter(word => word !== `@${MODIFIED_SETTING_TAG}`).join(' '); + this.searchWidget.setValue(queryWithoutModifiedTag); + } + this.searchWidget.focus(); + }, + dispose: () => { } + }; + } + + getActions(): IAction[] { + return [ + this.createModifiedAction(), + this.createAction( + 'extSettingsSearch', + localize('extSettingsSearch', "Extension ID"), + localize('extSettingsSearchTooltip', "Add extension ID filter"), + `@${EXTENSION_SETTING_TAG}`, + false + ), + this.createAction( + 'featuresSettingsSearch', + localize('featureSettingsSearch', "Feature"), + localize('featureSettingsSearchTooltip', "Add feature filter"), + `@${FEATURE_SETTING_TAG}`, + true + ), + this.createAction( + 'idSettingsSearch', + localize('idSettingsSearch', "Setting ID"), + localize('idSettingsSearchTooltip', "Add setting ID filter"), + `@${ID_SETTING_TAG}`, + false + ), + this.createAction( + 'langSettingsSearch', + localize('langSettingsSearch', "Language"), + localize('langSettingsSearchTooltip', "Add language ID filter"), + `@${LANGUAGE_SETTING_TAG}`, + true + ), + this.createAction( + 'tagSettingsSearch', + localize('tagSettingsSearch', "Tag"), + localize('tagSettingsSearchTooltip', "Add tag filter"), + `@${GENERAL_TAG_SETTING_TAG}`, + true + ), + ]; + } +} diff --git a/src/vs/workbench/contrib/preferences/common/preferences.ts b/src/vs/workbench/contrib/preferences/common/preferences.ts index ec3a14f1bf4..6138098d36f 100644 --- a/src/vs/workbench/contrib/preferences/common/preferences.ts +++ b/src/vs/workbench/contrib/preferences/common/preferences.ts @@ -42,6 +42,7 @@ export interface ISearchProvider { export const SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS = 'settings.action.clearSearchResults'; export const SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU = 'settings.action.showContextMenu'; +export const SETTINGS_EDITOR_COMMAND_SUGGEST_FILTERS = 'settings.action.suggestFilters'; export const CONTEXT_SETTINGS_EDITOR = new RawContextKey('inSettingsEditor', false); export const CONTEXT_SETTINGS_JSON_EDITOR = new RawContextKey('inSettingsJSONEditor', false); @@ -76,6 +77,7 @@ export const EXTENSION_SETTING_TAG = 'ext:'; export const FEATURE_SETTING_TAG = 'feature:'; export const ID_SETTING_TAG = 'id:'; export const LANGUAGE_SETTING_TAG = 'lang:'; +export const GENERAL_TAG_SETTING_TAG = 'tag:'; export const WORKSPACE_TRUST_SETTING_TAG = 'workspaceTrust'; export const REQUIRE_TRUSTED_WORKSPACE_SETTING_TAG = 'requireTrustedWorkspace'; export const KEYBOARD_LAYOUT_OPEN_PICKER = 'workbench.action.openKeyboardLayoutPicker'; From e3f21a18e2727ac37c78159ba81f2c6ee4a982cc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 13 Apr 2022 06:29:37 +0200 Subject: [PATCH 024/245] Toggle Tab Visibility keybind is too similar to Close File keybind (fix #147310) --- src/vs/workbench/browser/actions/layoutActions.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index a82c1828bb8..8440c0f8e12 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -501,13 +501,7 @@ registerAction2(class extends Action2 { original: 'Toggle Tab Visibility' }, category: CATEGORIES.View, - f1: true, - keybinding: { - weight: KeybindingWeight.WorkbenchContrib, - primary: undefined, - mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KeyW, }, - linux: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KeyW, } - } + f1: true }); } From 7e3c3e6e42fc0abaccc89ba84e40e36b8ccab5b9 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 13 Apr 2022 11:33:18 +0530 Subject: [PATCH 025/245] #68410 disable updating builtin extensions in stable --- .../extensions/browser/extensionsWorkbenchService.ts | 8 ++++++-- .../extensions/test/electron-browser/extension.test.ts | 8 ++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 50152dc1f51..701473fe624 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -206,6 +206,10 @@ export class Extension implements IExtension { if (!this.gallery || !this.local) { return false; } + // Do not allow updating system extensions in stable + if (this.type === ExtensionType.System && this.productService.quality === 'stable') { + return false; + } if (!this.local.preRelease && this.gallery.properties.isPreReleaseVersion) { return false; } @@ -1057,8 +1061,8 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension // Skip if check updates only for builtin extensions and current extension is not builtin. continue; } - if (installed.isBuiltin && !installed.local?.identifier.uuid) { - // Skip if the builtin extension does not have Marketplace id + if (installed.isBuiltin && (!installed.local?.identifier.uuid || this.productService.quality !== 'stable')) { + // Skip if the builtin extension does not have Marketplace identifier or the current quality is not stable. continue; } infos.push({ ...installed.identifier, preRelease: !!installed.local?.preRelease }); diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extension.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extension.test.ts index 335679af2a9..f07341fe991 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extension.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extension.test.ts @@ -12,6 +12,7 @@ import { URI } from 'vs/base/common/uri'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { generateUuid } from 'vs/base/common/uuid'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { IProductService } from 'vs/platform/product/common/productService'; suite('Extension Test', () => { @@ -19,6 +20,7 @@ suite('Extension Test', () => { setup(() => { instantiationService = new TestInstantiationService(); + instantiationService.stub(IProductService, >{ quality: 'insiders' }); }); test('extension is not outdated when there is no local and gallery', () => { @@ -51,6 +53,12 @@ suite('Extension Test', () => { assert.strictEqual(extension.outdated, true); }); + test('extension is not outdated when local is built in and older than gallery but product quality is stable', () => { + instantiationService.stub(IProductService, >{ quality: 'stable' }); + const extension = instantiationService.createInstance(Extension, () => ExtensionState.Installed, undefined, aLocalExtension('somext', { version: '1.0.0' }, { type: ExtensionType.System }), aGalleryExtension('somext', { version: '1.0.1' })); + assert.strictEqual(extension.outdated, false); + }); + test('extension is outdated when local and gallery are on same version but on different target platforms', () => { const extension = instantiationService.createInstance(Extension, () => ExtensionState.Installed, undefined, aLocalExtension('somext', {}, { targetPlatform: TargetPlatform.WIN32_IA32 }), aGalleryExtension('somext', {}, { targetPlatform: TargetPlatform.WIN32_X64 })); assert.strictEqual(extension.outdated, true); From eda166b7fa6377d2bd2fd2c33e5a844ea1b7bddd Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 13 Apr 2022 12:00:09 +0530 Subject: [PATCH 026/245] Move output service to workbench/services --- .../api/browser/mainThreadOutputService.ts | 3 +- .../workbench/api/common/extHost.protocol.ts | 2 +- src/vs/workbench/api/common/extHostOutput.ts | 2 +- .../contrib/debug/node/debugAdapter.ts | 2 +- .../browser/keybindings.contribution.ts | 2 +- .../contrib/logs/common/logs.contribution.ts | 3 +- .../contrib/clipboard/notebookClipboard.ts | 2 +- .../contrib/output/browser/logViewer.ts | 3 +- .../output/browser/output.contribution.ts | 3 +- .../output/browser/outputLinkProvider.ts | 2 +- .../contrib/output/browser/outputServices.ts | 3 +- .../contrib/output/browser/outputView.ts | 3 +- .../output/common/outputChannelModel.ts | 2 +- .../quickaccess/browser/viewQuickAccess.ts | 2 +- .../tasks/browser/abstractTaskService.ts | 2 +- .../tasks/browser/terminalTaskSystem.ts | 2 +- .../tasks/electron-sandbox/taskService.ts | 2 +- .../userDataSync/browser/userDataSync.ts | 2 +- .../services/output/common/output.ts | 138 ++++++++++++++++++ 19 files changed, 156 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadOutputService.ts b/src/vs/workbench/api/browser/mainThreadOutputService.ts index 2c82719f4e8..34859bb4235 100644 --- a/src/vs/workbench/api/browser/mainThreadOutputService.ts +++ b/src/vs/workbench/api/browser/mainThreadOutputService.ts @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Registry } from 'vs/platform/registry/common/platform'; -import { IOutputService, IOutputChannel, OUTPUT_VIEW_ID, OutputChannelUpdateMode } from 'vs/workbench/contrib/output/common/output'; -import { Extensions, IOutputChannelRegistry } from 'vs/workbench/services/output/common/output'; +import { Extensions, IOutputChannelRegistry, IOutputService, IOutputChannel, OUTPUT_VIEW_ID, OutputChannelUpdateMode } from 'vs/workbench/services/output/common/output'; import { MainThreadOutputServiceShape, MainContext, ExtHostOutputServiceShape, ExtHostContext } from '../common/extHost.protocol'; import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; import { UriComponents, URI } from 'vs/base/common/uri'; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index e28db7d5e55..53a77dc4d28 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -53,7 +53,7 @@ import * as notebookCommon from 'vs/workbench/contrib/notebook/common/notebookCo import { CellExecutionUpdateType } from 'vs/workbench/contrib/notebook/common/notebookExecutionService'; import { ICellExecutionComplete, ICellExecutionStateUpdate } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; -import { OutputChannelUpdateMode } from 'vs/workbench/contrib/output/common/output'; +import { OutputChannelUpdateMode } from 'vs/workbench/services/output/common/output'; import { InputValidationType } from 'vs/workbench/contrib/scm/common/scm'; import { IWorkspaceSymbol } from 'vs/workbench/contrib/search/common/search'; import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; diff --git a/src/vs/workbench/api/common/extHostOutput.ts b/src/vs/workbench/api/common/extHostOutput.ts index 12e6570161e..5c013fa2534 100644 --- a/src/vs/workbench/api/common/extHostOutput.ts +++ b/src/vs/workbench/api/common/extHostOutput.ts @@ -11,7 +11,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ILogger, ILoggerService } from 'vs/platform/log/common/log'; -import { OutputChannelUpdateMode } from 'vs/workbench/contrib/output/common/output'; +import { OutputChannelUpdateMode } from 'vs/workbench/services/output/common/output'; import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo'; diff --git a/src/vs/workbench/contrib/debug/node/debugAdapter.ts b/src/vs/workbench/contrib/debug/node/debugAdapter.ts index 6347d7ba2a5..4f0bf897787 100644 --- a/src/vs/workbench/contrib/debug/node/debugAdapter.ts +++ b/src/vs/workbench/contrib/debug/node/debugAdapter.ts @@ -13,7 +13,7 @@ import * as strings from 'vs/base/common/strings'; import * as objects from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; import { ExtensionsChannelId } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IOutputService } from 'vs/workbench/contrib/output/common/output'; +import { IOutputService } from 'vs/workbench/services/output/common/output'; import { IDebugAdapterExecutable, IDebuggerContribution, IPlatformSpecificAdapterContribution, IDebugAdapterServer, IDebugAdapterNamedPipeServer } from 'vs/workbench/contrib/debug/common/debug'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { AbstractDebugAdapter } from '../common/abstractDebugAdapter'; diff --git a/src/vs/workbench/contrib/keybindings/browser/keybindings.contribution.ts b/src/vs/workbench/contrib/keybindings/browser/keybindings.contribution.ts index 6fc249b03be..5f6cdfb3298 100644 --- a/src/vs/workbench/contrib/keybindings/browser/keybindings.contribution.ts +++ b/src/vs/workbench/contrib/keybindings/browser/keybindings.contribution.ts @@ -9,7 +9,7 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { CATEGORIES } from 'vs/workbench/common/actions'; import { rendererLogChannelId } from 'vs/workbench/contrib/logs/common/logConstants'; -import { IOutputService } from 'vs/workbench/contrib/output/common/output'; +import { IOutputService } from 'vs/workbench/services/output/common/output'; class ToggleKeybindingsLogAction extends Action2 { diff --git a/src/vs/workbench/contrib/logs/common/logs.contribution.ts b/src/vs/workbench/contrib/logs/common/logs.contribution.ts index 6cbfdc4cd7b..9485c762876 100644 --- a/src/vs/workbench/contrib/logs/common/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/common/logs.contribution.ts @@ -14,14 +14,13 @@ import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IFileService, whenProviderRegistered } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; -import { IOutputChannelRegistry, Extensions as OutputExt } from 'vs/workbench/services/output/common/output'; +import { IOutputChannelRegistry, Extensions as OutputExt, IOutputService } from 'vs/workbench/services/output/common/output'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { isWeb } from 'vs/base/common/platform'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { LogsDataCleaner } from 'vs/workbench/contrib/logs/common/logsDataCleaner'; -import { IOutputService } from 'vs/workbench/contrib/output/common/output'; import { supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; import { IProductService } from 'vs/platform/product/common/productService'; import { createCancelablePromise, timeout } from 'vs/base/common/async'; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/notebookClipboard.ts b/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/notebookClipboard.ts index 10037c96943..8fbc0999419 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/notebookClipboard.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/notebookClipboard.ts @@ -27,7 +27,7 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { RedoCommand, UndoCommand } from 'vs/editor/browser/editorExtensions'; import { IWebview } from 'vs/workbench/contrib/webview/browser/webview'; import { CATEGORIES } from 'vs/workbench/common/actions'; -import { IOutputService } from 'vs/workbench/contrib/output/common/output'; +import { IOutputService } from 'vs/workbench/services/output/common/output'; import { rendererLogChannelId } from 'vs/workbench/contrib/logs/common/logConstants'; import { ILogService } from 'vs/platform/log/common/log'; diff --git a/src/vs/workbench/contrib/output/browser/logViewer.ts b/src/vs/workbench/contrib/output/browser/logViewer.ts index a542e7dbe91..3c78aa114d1 100644 --- a/src/vs/workbench/contrib/output/browser/logViewer.ts +++ b/src/vs/workbench/contrib/output/browser/logViewer.ts @@ -15,8 +15,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput'; import { URI } from 'vs/base/common/uri'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { LOG_SCHEME } from 'vs/workbench/contrib/output/common/output'; -import { IFileOutputChannelDescriptor } from 'vs/workbench/services/output/common/output'; +import { LOG_SCHEME, IFileOutputChannelDescriptor } from 'vs/workbench/services/output/common/output'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; diff --git a/src/vs/workbench/contrib/output/browser/output.contribution.ts b/src/vs/workbench/contrib/output/browser/output.contribution.ts index b6dbbb8caf7..f9e4814794e 100644 --- a/src/vs/workbench/contrib/output/browser/output.contribution.ts +++ b/src/vs/workbench/contrib/output/browser/output.contribution.ts @@ -12,7 +12,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { OutputService, LogContentProvider } from 'vs/workbench/contrib/output/browser/outputServices'; -import { OUTPUT_MODE_ID, OUTPUT_MIME, OUTPUT_VIEW_ID, IOutputService, CONTEXT_IN_OUTPUT, LOG_SCHEME, LOG_MODE_ID, LOG_MIME, CONTEXT_ACTIVE_LOG_OUTPUT, CONTEXT_OUTPUT_SCROLL_LOCK } from 'vs/workbench/contrib/output/common/output'; +import { OUTPUT_MODE_ID, OUTPUT_MIME, OUTPUT_VIEW_ID, IOutputService, CONTEXT_IN_OUTPUT, LOG_SCHEME, LOG_MODE_ID, LOG_MIME, CONTEXT_ACTIVE_LOG_OUTPUT, CONTEXT_OUTPUT_SCROLL_LOCK, IOutputChannelDescriptor, IFileOutputChannelDescriptor } from 'vs/workbench/services/output/common/output'; import { OutputViewPane } from 'vs/workbench/contrib/output/browser/outputView'; import { IEditorPaneRegistry, EditorPaneDescriptor } from 'vs/workbench/browser/editor'; import { LogViewer, LogViewerInput } from 'vs/workbench/contrib/output/browser/logViewer'; @@ -25,7 +25,6 @@ import { ViewContainer, IViewContainersRegistry, ViewContainerLocation, Extensio import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { IQuickPickItem, IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { IOutputChannelDescriptor, IFileOutputChannelDescriptor } from 'vs/workbench/services/output/common/output'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { assertIsDefined } from 'vs/base/common/types'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; diff --git a/src/vs/workbench/contrib/output/browser/outputLinkProvider.ts b/src/vs/workbench/contrib/output/browser/outputLinkProvider.ts index eb346086655..ca06b37e21f 100644 --- a/src/vs/workbench/contrib/output/browser/outputLinkProvider.ts +++ b/src/vs/workbench/contrib/output/browser/outputLinkProvider.ts @@ -8,7 +8,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { IModelService } from 'vs/editor/common/services/model'; import { ILink } from 'vs/editor/common/languages'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { OUTPUT_MODE_ID, LOG_MODE_ID } from 'vs/workbench/contrib/output/common/output'; +import { OUTPUT_MODE_ID, LOG_MODE_ID } from 'vs/workbench/services/output/common/output'; import { MonacoWebWorker, createWebWorker } from 'vs/editor/browser/services/webWorker'; import { ICreateData, OutputLinkComputer } from 'vs/workbench/contrib/output/common/outputLinkComputer'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; diff --git a/src/vs/workbench/contrib/output/browser/outputServices.ts b/src/vs/workbench/contrib/output/browser/outputServices.ts index 4901c9f23ce..dffb7046911 100644 --- a/src/vs/workbench/contrib/output/browser/outputServices.ts +++ b/src/vs/workbench/contrib/output/browser/outputServices.ts @@ -9,8 +9,7 @@ import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IOutputChannel, IOutputService, OUTPUT_VIEW_ID, OUTPUT_SCHEME, LOG_SCHEME, LOG_MIME, OUTPUT_MIME, OutputChannelUpdateMode } from 'vs/workbench/contrib/output/common/output'; -import { IOutputChannelDescriptor, Extensions, IOutputChannelRegistry } from 'vs/workbench/services/output/common/output'; +import { IOutputChannel, IOutputService, OUTPUT_VIEW_ID, OUTPUT_SCHEME, LOG_SCHEME, LOG_MIME, OUTPUT_MIME, OutputChannelUpdateMode, IOutputChannelDescriptor, Extensions, IOutputChannelRegistry } from 'vs/workbench/services/output/common/output'; import { OutputLinkProvider } from 'vs/workbench/contrib/output/browser/outputLinkProvider'; import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; import { ITextModel } from 'vs/editor/common/model'; diff --git a/src/vs/workbench/contrib/output/browser/outputView.ts b/src/vs/workbench/contrib/output/browser/outputView.ts index ee9c7eadf5d..545d41fdcee 100644 --- a/src/vs/workbench/contrib/output/browser/outputView.ts +++ b/src/vs/workbench/contrib/output/browser/outputView.ts @@ -14,7 +14,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IEditorOpenContext } from 'vs/workbench/common/editor'; import { AbstractTextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor'; -import { OUTPUT_VIEW_ID, IOutputService, CONTEXT_IN_OUTPUT, IOutputChannel, CONTEXT_ACTIVE_LOG_OUTPUT, CONTEXT_OUTPUT_SCROLL_LOCK } from 'vs/workbench/contrib/output/common/output'; +import { OUTPUT_VIEW_ID, IOutputService, CONTEXT_IN_OUTPUT, IOutputChannel, CONTEXT_ACTIVE_LOG_OUTPUT, CONTEXT_OUTPUT_SCROLL_LOCK, IOutputChannelDescriptor, IOutputChannelRegistry, Extensions } from 'vs/workbench/services/output/common/output'; import { IThemeService, registerThemingParticipant, IColorTheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -27,7 +27,6 @@ import { IContextMenuService, IContextViewService } from 'vs/platform/contextvie import { IViewDescriptorService } from 'vs/workbench/common/views'; import { TextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IOutputChannelDescriptor, IOutputChannelRegistry, Extensions } from 'vs/workbench/services/output/common/output'; import { Registry } from 'vs/platform/registry/common/platform'; import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler'; import { ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox'; diff --git a/src/vs/workbench/contrib/output/common/outputChannelModel.ts b/src/vs/workbench/contrib/output/common/outputChannelModel.ts index cb24f441e21..4c01e67797c 100644 --- a/src/vs/workbench/contrib/output/common/outputChannelModel.ts +++ b/src/vs/workbench/contrib/output/common/outputChannelModel.ts @@ -21,7 +21,7 @@ import { Range } from 'vs/editor/common/core/range'; import { VSBuffer } from 'vs/base/common/buffer'; import { ILogger, ILoggerService, ILogService } from 'vs/platform/log/common/log'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; -import { OutputChannelUpdateMode } from 'vs/workbench/contrib/output/common/output'; +import { OutputChannelUpdateMode } from 'vs/workbench/services/output/common/output'; export interface IOutputChannelModel extends IDisposable { readonly onDispose: Event; diff --git a/src/vs/workbench/contrib/quickaccess/browser/viewQuickAccess.ts b/src/vs/workbench/contrib/quickaccess/browser/viewQuickAccess.ts index d1a3d7680d5..4a278af2146 100644 --- a/src/vs/workbench/contrib/quickaccess/browser/viewQuickAccess.ts +++ b/src/vs/workbench/contrib/quickaccess/browser/viewQuickAccess.ts @@ -7,7 +7,7 @@ import { localize } from 'vs/nls'; import { IQuickPickSeparator, IQuickInputService, ItemActivation } from 'vs/platform/quickinput/common/quickInput'; import { IPickerQuickAccessItem, PickerQuickAccessProvider } from 'vs/platform/quickinput/browser/pickerQuickAccess'; import { IViewDescriptorService, IViewsService, ViewContainer, ViewContainerLocation } from 'vs/workbench/common/views'; -import { IOutputService } from 'vs/workbench/contrib/output/common/output'; +import { IOutputService } from 'vs/workbench/services/output/common/output'; import { ITerminalGroupService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { PaneCompositeDescriptor } from 'vs/workbench/browser/panecomposite'; diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 70c269e4a69..2e0f5f76e40 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -41,7 +41,7 @@ import { IConfigurationResolverService } from 'vs/workbench/services/configurati import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder, IWorkspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IOutputService, IOutputChannel } from 'vs/workbench/contrib/output/common/output'; +import { IOutputService, IOutputChannel } from 'vs/workbench/services/output/common/output'; import { ITerminalGroupService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ITerminalProfileResolverService } from 'vs/workbench/contrib/terminal/common/terminal'; diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index c0547a2ae39..992ecb05aec 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -29,7 +29,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { ITerminalProfileResolverService, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal'; import { ITerminalService, ITerminalInstance, ITerminalGroupService } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { IOutputService } from 'vs/workbench/contrib/output/common/output'; +import { IOutputService } from 'vs/workbench/services/output/common/output'; import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEventKind, ProblemHandlingStrategy } from 'vs/workbench/contrib/tasks/common/problemCollectors'; import { Task, CustomTask, ContributedTask, RevealKind, CommandOptions, ShellConfiguration, RuntimeType, PanelKind, diff --git a/src/vs/workbench/contrib/tasks/electron-sandbox/taskService.ts b/src/vs/workbench/contrib/tasks/electron-sandbox/taskService.ts index 436f0194e19..a17d4185861 100644 --- a/src/vs/workbench/contrib/tasks/electron-sandbox/taskService.ts +++ b/src/vs/workbench/contrib/tasks/electron-sandbox/taskService.ts @@ -30,7 +30,7 @@ import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IViewsService, IViewDescriptorService } from 'vs/workbench/common/views'; -import { IOutputService } from 'vs/workbench/contrib/output/common/output'; +import { IOutputService } from 'vs/workbench/services/output/common/output'; import { ITerminalGroupService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 07d50613857..4a7fe8b6202 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -37,7 +37,7 @@ import { EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/ed import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import * as Constants from 'vs/workbench/contrib/logs/common/logConstants'; -import { IOutputService } from 'vs/workbench/contrib/output/common/output'; +import { IOutputService } from 'vs/workbench/services/output/common/output'; import { IActivityService, IBadge, NumberBadge, ProgressBadge } from 'vs/workbench/services/activity/common/activity'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; diff --git a/src/vs/workbench/services/output/common/output.ts b/src/vs/workbench/services/output/common/output.ts index f12fa430d7a..6adc8948c82 100644 --- a/src/vs/workbench/services/output/common/output.ts +++ b/src/vs/workbench/services/output/common/output.ts @@ -6,6 +6,144 @@ import { Event, Emitter } from 'vs/base/common/event'; import { Registry } from 'vs/platform/registry/common/platform'; import { URI } from 'vs/base/common/uri'; +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +/** + * Mime type used by the output editor. + */ +export const OUTPUT_MIME = 'text/x-code-output'; + +/** + * Output resource scheme. + */ +export const OUTPUT_SCHEME = 'output'; + +/** + * Id used by the output editor. + */ +export const OUTPUT_MODE_ID = 'Log'; + +/** + * Mime type used by the log output editor. + */ +export const LOG_MIME = 'text/x-code-log-output'; + +/** + * Log resource scheme. + */ +export const LOG_SCHEME = 'log'; + +/** + * Id used by the log output editor. + */ +export const LOG_MODE_ID = 'log'; + +/** + * Output view id + */ +export const OUTPUT_VIEW_ID = 'workbench.panel.output'; + +export const OUTPUT_SERVICE_ID = 'outputService'; + +export const MAX_OUTPUT_LENGTH = 10000 /* Max. number of output lines to show in output */ * 100 /* Guestimated chars per line */; + +export const CONTEXT_IN_OUTPUT = new RawContextKey('inOutput', false); + +export const CONTEXT_ACTIVE_LOG_OUTPUT = new RawContextKey('activeLogOutput', false); + +export const CONTEXT_OUTPUT_SCROLL_LOCK = new RawContextKey(`outputView.scrollLock`, false); + +export const IOutputService = createDecorator(OUTPUT_SERVICE_ID); + +/** + * The output service to manage output from the various processes running. + */ +export interface IOutputService { + readonly _serviceBrand: undefined; + + /** + * Given the channel id returns the output channel instance. + * Channel should be first registered via OutputChannelRegistry. + */ + getChannel(id: string): IOutputChannel | undefined; + + /** + * Given the channel id returns the registered output channel descriptor. + */ + getChannelDescriptor(id: string): IOutputChannelDescriptor | undefined; + + /** + * Returns an array of all known output channels descriptors. + */ + getChannelDescriptors(): IOutputChannelDescriptor[]; + + /** + * Returns the currently active channel. + * Only one channel can be active at a given moment. + */ + getActiveChannel(): IOutputChannel | undefined; + + /** + * Show the channel with the passed id. + */ + showChannel(id: string, preserveFocus?: boolean): Promise; + + /** + * Allows to register on active output channel change. + */ + onActiveOutputChannel: Event; +} + +export enum OutputChannelUpdateMode { + Append = 1, + Replace, + Clear +} + +export interface IOutputChannel { + + /** + * Identifier of the output channel. + */ + id: string; + + /** + * Label of the output channel to be displayed to the user. + */ + label: string; + + /** + * URI of the output channel. + */ + uri: URI; + + /** + * Appends output to the channel. + */ + append(output: string): void; + + /** + * Clears all received output for this channel. + */ + clear(): void; + + /** + * Replaces the content of the channel with given output + */ + replace(output: string): void; + + /** + * Update the channel. + */ + update(mode: OutputChannelUpdateMode.Append): void; + update(mode: OutputChannelUpdateMode, till: number): void; + + /** + * Disposes the output channel. + */ + dispose(): void; +} export const Extensions = { OutputChannels: 'workbench.contributions.outputChannels' From fa8d1063f6ab829e848575cf402d8bca74bcc2d4 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 13 Apr 2022 08:43:17 +0200 Subject: [PATCH 027/245] :up: 1.21.0 (#147348) * :up: `playwright` * fix install * adopt latest apis * comment --- .../common/installPlaywright.js | 2 +- .../common/installPlaywright.ts | 2 +- package.json | 2 +- src/vs/platform/driver/browser/driver.ts | 4 ++ src/vs/platform/driver/common/driver.ts | 3 +- src/vs/platform/driver/common/driverIpc.ts | 4 ++ .../platform/driver/electron-main/driver.ts | 6 +-- .../driver/electron-sandbox/driver.ts | 4 +- src/vs/platform/driver/node/driver.ts | 2 +- src/vs/workbench/electron-sandbox/window.ts | 6 +-- test/automation/src/code.ts | 43 +++++++++++-------- test/automation/src/electron.ts | 34 ++------------- test/automation/src/playwrightBrowser.ts | 30 +------------ test/automation/src/playwrightDriver.ts | 39 ++++++++--------- test/automation/src/playwrightElectron.ts | 7 ++- test/automation/src/processes.ts | 34 +++++++++++++++ yarn.lock | 18 ++++---- 17 files changed, 116 insertions(+), 124 deletions(-) create mode 100644 test/automation/src/processes.ts diff --git a/build/azure-pipelines/common/installPlaywright.js b/build/azure-pipelines/common/installPlaywright.js index 15f5898e7f7..af4bd5fb54c 100644 --- a/build/azure-pipelines/common/installPlaywright.js +++ b/build/azure-pipelines/common/installPlaywright.js @@ -5,7 +5,7 @@ *--------------------------------------------------------------------------------------------*/ Object.defineProperty(exports, "__esModule", { value: true }); const retry_1 = require("./retry"); -const { installDefaultBrowsersForNpmInstall } = require('playwright-core/lib/utils/registry'); +const { installDefaultBrowsersForNpmInstall } = require('playwright-core/lib/server'); async function install() { await (0, retry_1.retry)(() => installDefaultBrowsersForNpmInstall()); } diff --git a/build/azure-pipelines/common/installPlaywright.ts b/build/azure-pipelines/common/installPlaywright.ts index a99ab3aeec1..5d837a55413 100644 --- a/build/azure-pipelines/common/installPlaywright.ts +++ b/build/azure-pipelines/common/installPlaywright.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { retry } from './retry'; -const { installDefaultBrowsersForNpmInstall } = require('playwright-core/lib/utils/registry'); +const { installDefaultBrowsersForNpmInstall } = require('playwright-core/lib/server'); async function install() { await retry(() => installDefaultBrowsersForNpmInstall()); diff --git a/package.json b/package.json index 3eaa24c2254..c0f1723d171 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,7 @@ }, "devDependencies": { "7zip": "0.0.6", - "@playwright/test": "1.20.2", + "@playwright/test": "1.21.0", "@types/applicationinsights": "0.20.0", "@types/cookie": "^0.3.3", "@types/copy-webpack-plugin": "^6.0.3", diff --git a/src/vs/platform/driver/browser/driver.ts b/src/vs/platform/driver/browser/driver.ts index c57855c3828..c16032c953f 100644 --- a/src/vs/platform/driver/browser/driver.ts +++ b/src/vs/platform/driver/browser/driver.ts @@ -205,6 +205,10 @@ export class BrowserWindowDriver implements IWindowDriver { throw new Error('Method not implemented.'); } + + async exitApplication(): Promise { + // No-op in web + } } export function registerWindowDriver(): void { diff --git a/src/vs/platform/driver/common/driver.ts b/src/vs/platform/driver/common/driver.ts index 264ed83ec8b..8a573487f6d 100644 --- a/src/vs/platform/driver/common/driver.ts +++ b/src/vs/platform/driver/common/driver.ts @@ -42,7 +42,7 @@ export interface IDriver { getWindowIds(): Promise; startTracing(windowId: number, name: string): Promise; stopTracing(windowId: number, name: string, persist: boolean): Promise; - exitApplication(): Promise; + exitApplication(): Promise; dispatchKeybinding(windowId: number, keybinding: string): Promise; click(windowId: number, selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise; setValue(windowId: number, selector: string, text: string): Promise; @@ -69,6 +69,7 @@ export interface IWindowDriver { writeInTerminal(selector: string, text: string): Promise; getLocaleInfo(): Promise; getLocalizedStrings(): Promise; + exitApplication(): Promise; } //*END diff --git a/src/vs/platform/driver/common/driverIpc.ts b/src/vs/platform/driver/common/driverIpc.ts index 6d5ed5e55c4..dc095ee06fc 100644 --- a/src/vs/platform/driver/common/driverIpc.ts +++ b/src/vs/platform/driver/common/driverIpc.ts @@ -83,6 +83,10 @@ export class WindowDriverChannelClient implements IWindowDriver { getLocalizedStrings(): Promise { return this.channel.call('getLocalizedStrings'); } + + exitApplication(): Promise { + return this.channel.call('exitApplication'); + } } export class WindowDriverRegistryChannelClient implements IWindowDriverRegistry { diff --git a/src/vs/platform/driver/electron-main/driver.ts b/src/vs/platform/driver/electron-main/driver.ts index c071bf0cf1c..0e6956956b8 100644 --- a/src/vs/platform/driver/electron-main/driver.ts +++ b/src/vs/platform/driver/electron-main/driver.ts @@ -96,12 +96,10 @@ export class Driver implements IDriver, IWindowDriverRegistry { return image.toPNG().toString('base64'); } - async exitApplication(): Promise { + async exitApplication(): Promise { this.logService.info(`[driver] exitApplication()`); - this.lifecycleMainService.quit(); - - return process.pid; + await this.lifecycleMainService.quit(); } async dispatchKeybinding(windowId: number, keybinding: string): Promise { diff --git a/src/vs/platform/driver/electron-sandbox/driver.ts b/src/vs/platform/driver/electron-sandbox/driver.ts index bc2e4c9ae38..b398749230b 100644 --- a/src/vs/platform/driver/electron-sandbox/driver.ts +++ b/src/vs/platform/driver/electron-sandbox/driver.ts @@ -12,7 +12,7 @@ import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; interface INativeWindowDriverHelper { - exitApplication(): Promise; + exitApplication(): Promise; } class NativeWindowDriver extends BrowserWindowDriver { @@ -21,7 +21,7 @@ class NativeWindowDriver extends BrowserWindowDriver { super(); } - exitApplication(): Promise { + override exitApplication(): Promise { return this.helper.exitApplication(); } } diff --git a/src/vs/platform/driver/node/driver.ts b/src/vs/platform/driver/node/driver.ts index 1542e503f1d..6ce00b50e53 100644 --- a/src/vs/platform/driver/node/driver.ts +++ b/src/vs/platform/driver/node/driver.ts @@ -59,7 +59,7 @@ export class DriverChannelClient implements IDriver { return this.channel.call('stopTracing', [windowId, name, persist]); } - exitApplication(): Promise { + exitApplication(): Promise { return this.channel.call('exitApplication'); } diff --git a/src/vs/workbench/electron-sandbox/window.ts b/src/vs/workbench/electron-sandbox/window.ts index 9e53d9f7590..8617e328264 100644 --- a/src/vs/workbench/electron-sandbox/window.ts +++ b/src/vs/workbench/electron-sandbox/window.ts @@ -649,10 +649,8 @@ export class NativeWindow extends Disposable { if (this.environmentService.enableSmokeTestDriver) { const that = this; registerWindowDriver({ - async exitApplication(): Promise { - that.nativeHostService.quit(); - - return that.environmentService.mainPid; + async exitApplication(): Promise { + return that.nativeHostService.quit(); } }); } diff --git a/test/automation/src/code.ts b/test/automation/src/code.ts index 4ffdd0ae203..f50d69e83fa 100644 --- a/test/automation/src/code.ts +++ b/test/automation/src/code.ts @@ -13,6 +13,7 @@ import { launch as launchPlaywrightElectron } from './playwrightElectron'; import { Logger, measureAndLog } from './logger'; import { copyExtension } from './extensions'; import * as treekill from 'tree-kill'; +import { teardown } from './processes'; const rootPath = join(__dirname, '../../..'); @@ -39,8 +40,8 @@ interface ICodeInstance { const instances = new Set(); -function registerInstance(process: cp.ChildProcess, logger: Logger, type: string, kill: () => Promise) { - const instance = { kill }; +function registerInstance(process: cp.ChildProcess, logger: Logger, type: string) { + const instance = { kill: () => teardown(process, logger) }; instances.add(instance); process.stdout?.on('data', data => logger.log(`[${type}] stdout: ${data}`)); @@ -53,7 +54,7 @@ function registerInstance(process: cp.ChildProcess, logger: Logger, type: string }); } -async function teardown(signal?: number) { +async function teardownAll(signal?: number) { stopped = true; for (const instance of instances) { @@ -66,9 +67,9 @@ async function teardown(signal?: number) { } let stopped = false; -process.on('exit', () => teardown()); -process.on('SIGINT', () => teardown(128 + 2)); // https://nodejs.org/docs/v14.16.0/api/process.html#process_signal_events -process.on('SIGTERM', () => teardown(128 + 15)); // same as above +process.on('exit', () => teardownAll()); +process.on('SIGINT', () => teardownAll(128 + 2)); // https://nodejs.org/docs/v14.16.0/api/process.html#process_signal_events +process.on('SIGTERM', () => teardownAll(128 + 15)); // same as above export async function launch(options: LaunchOptions): Promise { if (stopped) { @@ -79,25 +80,26 @@ export async function launch(options: LaunchOptions): Promise { // Browser smoke tests if (options.web) { - const { serverProcess, client, driver, kill } = await measureAndLog(launchPlaywrightBrowser(options), 'launch playwright (browser)', options.logger); - registerInstance(serverProcess, options.logger, 'server', kill); + const { serverProcess, client, driver } = await measureAndLog(launchPlaywrightBrowser(options), 'launch playwright (browser)', options.logger); + registerInstance(serverProcess, options.logger, 'server'); - return new Code(client, driver, options.logger); + return new Code(client, driver, options.logger, serverProcess); } // Electron smoke tests (playwright) else if (!options.legacy) { - const { client, driver } = await measureAndLog(launchPlaywrightElectron(options), 'launch playwright (electron)', options.logger); + const { electronProcess, client, driver } = await measureAndLog(launchPlaywrightElectron(options), 'launch playwright (electron)', options.logger); + registerInstance(electronProcess, options.logger, 'electron'); - return new Code(client, driver, options.logger); + return new Code(client, driver, options.logger, electronProcess); } // Electron smoke tests (legacy driver) else { - const { electronProcess, client, driver, kill } = await measureAndLog(launchElectron(options), 'launch electron', options.logger); - registerInstance(electronProcess, options.logger, 'electron', kill); + const { electronProcess, client, driver } = await measureAndLog(launchElectron(options), 'launch electron', options.logger); + registerInstance(electronProcess, options.logger, 'electron'); - return new Code(client, driver, options.logger); + return new Code(client, driver, options.logger, electronProcess); } } @@ -109,7 +111,8 @@ export class Code { constructor( private client: IDisposable, driver: IDriver, - readonly logger: Logger + readonly logger: Logger, + private readonly mainProcess: cp.ChildProcess ) { this.driver = new Proxy(driver, { get(target, prop) { @@ -150,13 +153,15 @@ export class Code { } async exit(): Promise { - - // Start the exit flow via driver - const pid = await measureAndLog(this.driver.exitApplication(), 'driver.exitApplication()', this.logger); - return measureAndLog(new Promise((resolve, reject) => { + const pid = this.mainProcess.pid!; + let done = false; + // Start the exit flow via driver + this.driver.exitApplication(); + + // Await the exit of the application (async () => { let retries = 0; while (!done) { diff --git a/test/automation/src/electron.ts b/test/automation/src/electron.ts index 8f9f8dcd8f9..efc8ff8da7d 100644 --- a/test/automation/src/electron.ts +++ b/test/automation/src/electron.ts @@ -10,10 +10,10 @@ import { connect as connectElectronDriver, IDisposable, IDriver } from './driver import { ChildProcess, spawn, SpawnOptions } from 'child_process'; import * as mkdirp from 'mkdirp'; import { promisify } from 'util'; -import * as kill from 'tree-kill'; +import { teardown } from './processes'; import { copyExtension } from './extensions'; import { URI } from 'vscode-uri'; -import { Logger, measureAndLog } from './logger'; +import { measureAndLog } from './logger'; import type { LaunchOptions } from './code'; const root = join(__dirname, '..', '..', '..'); @@ -99,7 +99,7 @@ export async function resolveElectronConfiguration(options: LaunchOptions): Prom /** * @deprecated should use the playwright based electron support instead */ -export async function launch(options: LaunchOptions): Promise<{ electronProcess: ChildProcess; client: IDisposable; driver: IDriver; kill: () => Promise }> { +export async function launch(options: LaunchOptions): Promise<{ electronProcess: ChildProcess; client: IDisposable; driver: IDriver }> { const { codePath, logger, verbose } = options; const { env, args, electronPath } = await resolveElectronConfiguration(options); @@ -126,8 +126,7 @@ export async function launch(options: LaunchOptions): Promise<{ electronProcess: return { electronProcess, client, - driver, - kill: () => teardown(electronProcess, options.logger) + driver }; } catch (err) { @@ -152,31 +151,6 @@ export async function launch(options: LaunchOptions): Promise<{ electronProcess: } } -async function teardown(electronProcess: ChildProcess, logger: Logger): Promise { - const electronPid = electronProcess.pid; - if (typeof electronPid !== 'number') { - return; - } - - let retries = 0; - while (retries < 3) { - retries++; - - try { - return await promisify(kill)(electronPid); - } catch (error) { - try { - process.kill(electronPid, 0); // throws an exception if the process doesn't exist anymore - logger.log(`Error tearing down electron client (pid: ${electronPid}, attempt: ${retries}): ${error}`); - } catch (error) { - return; // Expected when process is gone - } - } - } - - logger.log(`Gave up tearing down electron client after ${retries} attempts...`); -} - export function getDevElectronPath(): string { const buildPath = join(root, '.build'); const product = require(join(root, 'product.json')); diff --git a/test/automation/src/playwrightBrowser.ts b/test/automation/src/playwrightBrowser.ts index a2851762385..e594efe6688 100644 --- a/test/automation/src/playwrightBrowser.ts +++ b/test/automation/src/playwrightBrowser.ts @@ -10,7 +10,6 @@ import { mkdir } from 'fs'; import { promisify } from 'util'; import { IDriver, IDisposable } from './driver'; import { URI } from 'vscode-uri'; -import * as kill from 'tree-kill'; import { Logger, measureAndLog } from './logger'; import type { LaunchOptions } from './code'; import { PlaywrightDriver } from './playwrightDriver'; @@ -19,7 +18,7 @@ const root = join(__dirname, '..', '..', '..'); let port = 9000; -export async function launch(options: LaunchOptions): Promise<{ serverProcess: ChildProcess; client: IDisposable; driver: IDriver; kill: () => Promise }> { +export async function launch(options: LaunchOptions): Promise<{ serverProcess: ChildProcess; client: IDisposable; driver: IDriver }> { // Launch server const { serverProcess, endpoint } = await launchServer(options); @@ -32,8 +31,7 @@ export async function launch(options: LaunchOptions): Promise<{ serverProcess: C client: { dispose: () => { /* there is no client to dispose for browser, teardown is triggered via exitApplication call */ } }, - driver: new PlaywrightDriver(browser, context, page, serverProcess.pid, options), - kill: () => teardown(serverProcess.pid, options.logger) + driver: new PlaywrightDriver(browser, context, page, serverProcess, options) }; } @@ -145,30 +143,6 @@ async function launchBrowser(options: LaunchOptions, endpoint: string) { return { browser, context, page }; } -export async function teardown(serverPid: number | undefined, logger: Logger): Promise { - if (typeof serverPid !== 'number') { - return; - } - - let retries = 0; - while (retries < 3) { - retries++; - - try { - return await promisify(kill)(serverPid); - } catch (error) { - try { - process.kill(serverPid, 0); // throws an exception if the process doesn't exist anymore - logger.log(`Error tearing down server (pid: ${serverPid}, attempt: ${retries}): ${error}`); - } catch (error) { - return; // Expected when process is gone - } - } - } - - logger.log(`Gave up tearing down server after ${retries} attempts...`); -} - function waitForEndpoint(server: ChildProcess, logger: Logger): Promise { return new Promise((resolve, reject) => { let endpointFound = false; diff --git a/test/automation/src/playwrightDriver.ts b/test/automation/src/playwrightDriver.ts index 6a28ed401fa..8ff2db7b1b3 100644 --- a/test/automation/src/playwrightDriver.ts +++ b/test/automation/src/playwrightDriver.ts @@ -9,7 +9,8 @@ import { IDriver, IWindowDriver } from './driver'; import { PageFunction } from 'playwright-core/types/structs'; import { measureAndLog } from './logger'; import { LaunchOptions } from './code'; -import { teardown } from './playwrightBrowser'; +import { teardown } from './processes'; +import { ChildProcess } from 'child_process'; export class PlaywrightDriver implements IDriver { @@ -36,7 +37,7 @@ export class PlaywrightDriver implements IDriver { private readonly application: playwright.Browser | playwright.ElectronApplication, private readonly context: playwright.BrowserContext, private readonly page: playwright.Page, - private readonly serverPid: number | undefined, + private readonly serverProcess: ChildProcess | undefined, private readonly options: LaunchOptions ) { } @@ -107,32 +108,28 @@ export class PlaywrightDriver implements IDriver { // Ignore } - // VSCode shutdown (desktop only) - let mainPid: number | undefined = undefined; - if (!this.options.web) { + // Web: exit via `close` method + if (this.options.web) { try { - mainPid = await measureAndLog(this._evaluateWithDriver(([driver]) => (driver as unknown as IDriver).exitApplication()), 'driver.exitApplication()', this.options.logger); + await measureAndLog(this.application.close(), 'playwright.close()', this.options.logger); + } catch (error) { + this.options.logger.log(`Error closing appliction (${error})`); + } + } + + // Desktop: exit via `driver.exitApplication` + else { + try { + await measureAndLog(this._evaluateWithDriver(([driver]) => driver.exitApplication()), 'driver.exitApplication()', this.options.logger); } catch (error) { this.options.logger.log(`Error exiting appliction (${error})`); } } - // Playwright shutdown - try { - await Promise.race([ - measureAndLog(this.application.close(), 'playwright.close()', this.options.logger), - new Promise(resolve => setTimeout(() => resolve(), 10000)) // TODO@bpasero mitigate https://github.com/microsoft/vscode/issues/146803 - ]); - } catch (error) { - this.options.logger.log(`Error closing appliction (${error})`); + // Server: via `teardown` + if (this.serverProcess) { + await measureAndLog(teardown(this.serverProcess, this.options.logger), 'teardown server process', this.options.logger); } - - // Server shutdown - if (typeof this.serverPid === 'number') { - await measureAndLog(teardown(this.serverPid, this.options.logger), 'teardown server', this.options.logger); - } - - return mainPid ?? this.serverPid! /* when running web we must have a server Pid */; } async dispatchKeybinding(windowId: number, keybinding: string) { diff --git a/test/automation/src/playwrightElectron.ts b/test/automation/src/playwrightElectron.ts index 56677298146..1569841fdae 100644 --- a/test/automation/src/playwrightElectron.ts +++ b/test/automation/src/playwrightElectron.ts @@ -9,8 +9,9 @@ import type { LaunchOptions } from './code'; import { PlaywrightDriver } from './playwrightDriver'; import { IElectronConfiguration, resolveElectronConfiguration } from './electron'; import { measureAndLog } from './logger'; +import { ChildProcess } from 'child_process'; -export async function launch(options: LaunchOptions): Promise<{ client: IDisposable; driver: IDriver }> { +export async function launch(options: LaunchOptions): Promise<{ electronProcess: ChildProcess; client: IDisposable; driver: IDriver }> { // Resolve electron config and update const { electronPath, args, env } = await resolveElectronConfiguration(options); @@ -18,12 +19,14 @@ export async function launch(options: LaunchOptions): Promise<{ client: IDisposa // Launch electron via playwright const { electron, context, page } = await launchElectron({ electronPath, args, env }, options); + const electronProcess = electron.process(); return { + electronProcess, client: { dispose: () => { /* there is no client to dispose for electron, teardown is triggered via exitApplication call */ } }, - driver: new PlaywrightDriver(electron, context, page, undefined /* no server */, options) + driver: new PlaywrightDriver(electron, context, page, undefined /* no server process */, options) }; } diff --git a/test/automation/src/processes.ts b/test/automation/src/processes.ts new file mode 100644 index 00000000000..17dcb79b32b --- /dev/null +++ b/test/automation/src/processes.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ChildProcess } from 'child_process'; +import { promisify } from 'util'; +import * as treekill from 'tree-kill'; +import { Logger } from './logger'; + +export async function teardown(p: ChildProcess, logger: Logger, retryCount = 3): Promise { + const pid = p.pid; + if (typeof pid !== 'number') { + return; + } + + let retries = 0; + while (retries < retryCount) { + retries++; + + try { + return await promisify(treekill)(pid); + } catch (error) { + try { + process.kill(pid, 0); // throws an exception if the process doesn't exist anymore + logger.log(`Error tearing down process (pid: ${pid}, attempt: ${retries}): ${error}`); + } catch (error) { + return; // Expected when process is gone + } + } + } + + logger.log(`Gave up tearing down process client after ${retries} attempts...`); +} diff --git a/yarn.lock b/yarn.lock index b2929b3d0a8..782aba6424d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1046,10 +1046,10 @@ node-addon-api "^3.2.1" node-gyp-build "^4.3.0" -"@playwright/test@1.20.2": - version "1.20.2" - resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.20.2.tgz#0da1f24bf12d5a7249fa771a5344b76170f62653" - integrity sha512-unkLa+xe/lP7MVC0qpgadc9iSG1+LEyGBzlXhGS/vLGAJaSFs8DNfI89hNd5shHjWfNzb34JgPVnkRKCSNo5iw== +"@playwright/test@1.21.0": + version "1.21.0" + resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.21.0.tgz#611dd3f469c539e5be3a764395effa40735742a6" + integrity sha512-jvgN3ZeAG6rw85z4u9Rc4uyj6qIaYlq2xrOtS7J2+CDYhzKOttab9ix9ELcvBOCHuQ6wgTfxfJYdh6DRZmQ9hg== dependencies: "@babel/code-frame" "7.16.7" "@babel/core" "7.16.12" @@ -1080,7 +1080,7 @@ ms "2.1.3" open "8.4.0" pirates "4.0.4" - playwright-core "1.20.2" + playwright-core "1.21.0" rimraf "3.0.2" source-map-support "0.4.18" stack-utils "2.0.5" @@ -9002,10 +9002,10 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -playwright-core@1.20.2: - version "1.20.2" - resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.20.2.tgz#02336afd9a631d59a666f11f3492550201c6c31b" - integrity sha512-iV6+HftSPalynkq0CYJala1vaTOq7+gU9BRfKCdM9bAxNq/lFLrwbluug2Wt5OoUwbMABcnTThIEm3/qUhCdJQ== +playwright-core@1.21.0: + version "1.21.0" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.21.0.tgz#1b68e87f4fd2fc5653def1e61ccdef6845c604a6" + integrity sha512-yDGVs9qaaW6WiefgR7wH1CGt9D6D/X4U3jNpIzH0FjjrrWLCOYQo78Tu3SkW8X+/kWlBpj49iWf3QNSxhYc12Q== dependencies: colors "1.4.0" commander "8.3.0" From 711b0708690159cf8e9a5205ad97d39e0d47d424 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 13 Apr 2022 11:01:23 +0200 Subject: [PATCH 028/245] Fixes #147329. --- .../contrib/debug/browser/breakpointEditorContribution.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts index 762b74c8201..7d8351ddb7d 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts @@ -485,7 +485,6 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi inlineWidget }; }); - } finally { this.ignoreDecorationsChangedEvent = false; } @@ -511,6 +510,12 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi inlineWidget }; }); + + for (const d of this.breakpointDecorations) { + if (d.inlineWidget) { + this.editor.layoutContentWidget(d.inlineWidget); + } + } } private async onModelDecorationsChanged(): Promise { From 86f20b3ecda19641e833dc7d1c41bcdec16ff715 Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 13 Apr 2022 11:31:56 +0200 Subject: [PATCH 029/245] up distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c0f1723d171..f2996e6d45b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.67.0", - "distro": "3b8074f0f1fad5340a89fadbcfe9d2b3f6869baf", + "distro": "5589eefba75be18bb4b240d9e45efd88efd782e9", "author": { "name": "Microsoft Corporation" }, From b65d988dfcf36a28733dcecc0fcad7138ceca69d Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 13 Apr 2022 11:35:22 +0200 Subject: [PATCH 030/245] Improves documentation for proposed inline completions. --- .../vscode.proposed.inlineCompletionsNew.d.ts | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts b/src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts index 8ab631571c8..4d78afaaa94 100644 --- a/src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts +++ b/src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts @@ -59,7 +59,7 @@ declare module 'vscode' { * the inline completion must also replace `.` and start with `.log`, for example `.log()`. * * Inline completion providers are requested again whenever the selected item changes. - */ + */ readonly selectedCompletionInfo: SelectedCompletionInfoNew | undefined; } @@ -69,12 +69,12 @@ declare module 'vscode' { export interface SelectedCompletionInfoNew { /** * The range that will be replaced if this completion item is accepted. - */ + */ range: Range; /** * The text the range will be replaced with if this completion is accepted. - */ + */ text: string; } @@ -122,13 +122,7 @@ declare module 'vscode' { /** * The text to replace the range with. Must be set. * Is used both for the preview and the accept operation. - * - * The text the range refers to must be a subword of this value (`AB` and `BEF` are subwords of `ABCDEF`, but `Ab` is not). - * Additionally, if possible, it should be a prefix of this value for a better user-experience. - * - * However, any indentation of the text to replace does not matter for the subword constraint. - * Thus, ` B` can be replaced with ` ABC`, effectively removing a whitespace and inserting `A` and `C`. - */ + */ insertText: string | SnippetString; /** @@ -145,7 +139,7 @@ declare module 'vscode' { * Instead of reporting a completion that inserts an extension at the end of a word, * the whole word (or even the whole line) should be replaced with the extended word (or extended line) to improve the UX. * That way, when the user presses backspace, the cache can be reused and there is no flickering. - */ + */ range?: Range; /** From 534aacf7f27c7b262df243ebb2eeebdf50bc4043 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 13 Apr 2022 11:54:47 +0200 Subject: [PATCH 031/245] Change default of comments.openView to firstFile Fixes #147365 --- .../comments/browser/comments.contribution.ts | 6 +++--- .../browser/commentsEditorContribution.ts | 20 +++++++++++++++---- .../contrib/comments/browser/commentsView.ts | 6 +++++- .../comments/common/commentsConfiguration.ts | 2 +- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/comments.contribution.ts b/src/vs/workbench/contrib/comments/browser/comments.contribution.ts index 0d515f2ae79..2ba619a7c0c 100644 --- a/src/vs/workbench/contrib/comments/browser/comments.contribution.ts +++ b/src/vs/workbench/contrib/comments/browser/comments.contribution.ts @@ -24,9 +24,9 @@ Registry.as(ConfigurationExtensions.Configuration).regis markdownDeprecationMessage: nls.localize('comments.openPanel.deprecated', "This setting is deprecated in favor of `comments.openView`.") }, 'comments.openView': { - enum: ['never', 'file'], - enumDescriptions: [nls.localize('comments.openView.never', "The comments view will never be opened."), nls.localize('comments.openView.file', "The comments view will open when a file with comments is active.")], - default: 'file', + enum: ['never', 'file', 'firstFile'], + enumDescriptions: [nls.localize('comments.openView.never', "The comments view will never be opened."), nls.localize('comments.openView.file', "The comments view will open when a file with comments is active."), nls.localize('comments.openView.firstFile', "If the comments view has not been opened yet during this session it will open the first time during a session that a file with comments is active.")], + default: 'firstFile', description: nls.localize('comments.openView', "Controls when the comments view should open."), restricted: false }, diff --git a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts index 0fa7eaf9ded..14f1f730236 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts @@ -49,6 +49,7 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { CommentThreadRangeDecorator } from 'vs/workbench/contrib/comments/browser/commentThreadRangeDecorator'; import { commentThreadRangeBackground, commentThreadRangeBorder } from 'vs/workbench/contrib/comments/browser/commentColors'; import { ICursorSelectionChangedEvent } from 'vs/editor/common/cursorEvents'; +import { CommentsPanel } from 'vs/workbench/contrib/comments/browser/commentsView'; export const ID = 'editor.contrib.review'; @@ -633,13 +634,24 @@ export class CommentController implements IEditorContribution { })); this.beginCompute().then(() => { - if (this._commentWidgets.length - && (this.configurationService.getValue(COMMENTS_SECTION).openView === 'file')) { - this.viewsService.openView(COMMENTS_VIEW_ID); - } + return this.openCommentsView(); }); } + private async openCommentsView() { + if (this._commentWidgets.length) { + if (this.configurationService.getValue(COMMENTS_SECTION).openView === 'file') { + return this.viewsService.openView(COMMENTS_VIEW_ID); + } else if (this.configurationService.getValue(COMMENTS_SECTION).openView === 'firstFile') { + const hasShownView = this.viewsService.getViewWithId(COMMENTS_VIEW_ID)?.hasRendered; + if (!hasShownView) { + return this.viewsService.openView(COMMENTS_VIEW_ID); + } + } + } + return undefined; + } + private displayCommentThread(owner: string, thread: languages.CommentThread, pendingComment: string | null): void { const zoneWidget = this.instantiationService.createInstance(ReviewZoneWidget, this.editor, owner, thread, pendingComment); zoneWidget.display(thread.range.endLineNumber); diff --git a/src/vs/workbench/contrib/comments/browser/commentsView.ts b/src/vs/workbench/contrib/comments/browser/commentsView.ts index ad852ea3b7e..7077d35338b 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsView.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsView.ts @@ -120,7 +120,7 @@ export class CommentsPanel extends ViewPane { const focusColor = theme.getColor(focusBorder); if (focusColor) { - content.push(`.comments-panel .commenst-panel-container a:focus { outline-color: ${focusColor}; }`); + content.push(`.comments-panel .comments-panel-container a:focus { outline-color: ${focusColor}; }`); } const codeTextForegroundColor = theme.getColor(textPreformatForeground); @@ -147,6 +147,10 @@ export class CommentsPanel extends ViewPane { } } + public get hasRendered(): boolean { + return !!this.tree; + } + public override layoutBody(height: number, width: number): void { super.layoutBody(height, width); this.tree.layout(height, width); diff --git a/src/vs/workbench/contrib/comments/common/commentsConfiguration.ts b/src/vs/workbench/contrib/comments/common/commentsConfiguration.ts index 1ef2ba67cb7..d3c180e69a4 100644 --- a/src/vs/workbench/contrib/comments/common/commentsConfiguration.ts +++ b/src/vs/workbench/contrib/comments/common/commentsConfiguration.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ export interface ICommentsConfiguration { - openView: 'never' | 'file'; + openView: 'never' | 'file' | 'firstFile'; useRelativeTime: boolean; } From 083d53471cca59d2a025b4729e7dd1c2730d7c0b Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 13 Apr 2022 12:27:35 +0200 Subject: [PATCH 032/245] small tweaks, some todos/questions --- .../vscode.proposed.inlineCompletionsNew.d.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts b/src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts index 4d78afaaa94..cdaf99b812a 100644 --- a/src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts +++ b/src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts @@ -37,7 +37,9 @@ declare module 'vscode' { * If inline completions are enabled, this method will be called whenever the user stopped typing. * It will also be called when the user explicitly triggers inline completions or asks for the next or previous inline completion. * `context.triggerKind` can be used to distinguish between these scenarios. - */ + */ + // TODO@API clarify "or asks for the next or previous inline completion"? Why would I return N items in the first place? + // TODO@API jsdoc for args, return-type provideInlineCompletionItems(document: TextDocument, position: Position, context: InlineCompletionContextNew, token: CancellationToken): ProviderResult; } @@ -70,12 +72,12 @@ declare module 'vscode' { /** * The range that will be replaced if this completion item is accepted. */ - range: Range; + readonly range: Range; /** * The text the range will be replaced with if this completion is accepted. */ - text: string; + readonly text: string; } /** @@ -110,6 +112,7 @@ declare module 'vscode' { */ commands?: Command[]; + // TODO@API jsdocs constructor(items: InlineCompletionItemNew[], commands?: Command[]); } @@ -126,7 +129,9 @@ declare module 'vscode' { insertText: string | SnippetString; /** - * A text that is used to decide if this inline completion should be shown. + * A text that is used to decide if this inline completion should be shown. When `falsy` + * the {@link InlineCompletionItemNew.insertText} is used. + * * An inline completion is shown if the text to replace is a prefix of the filter text. */ filterText?: string; @@ -135,6 +140,7 @@ declare module 'vscode' { * The range to replace. * Must begin and end on the same line. * + * TODO@API caching is an imlementation detail. drop that explanation? * Prefer replacements over insertions to avoid cache invalidation: * Instead of reporting a completion that inserts an extension at the end of a word, * the whole word (or even the whole line) should be replaced with the extended word (or extended line) to improve the UX. From 622d0587fa84ec16668cd0a08026b08dbcb80482 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 13 Apr 2022 12:45:30 +0200 Subject: [PATCH 033/245] explicitly set multiplpeSelectionSupport to false for suggest Result: list does not set aria-multiselect on the suggest widget (confuses VoiceOver) fyi @jrieken --- src/vs/editor/contrib/suggest/browser/suggestWidget.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts index 04e6390ba9c..8ac347d22b9 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts @@ -223,6 +223,7 @@ export class SuggestWidget implements IDisposable { alwaysConsumeMouseWheel: true, useShadows: false, mouseSupport: false, + multipleSelectionSupport: false, accessibilityProvider: { getRole: () => 'option', getWidgetAriaLabel: () => nls.localize('suggest', "Suggest"), From a7cdd9a391f3737a57f54838ed2a6918cc051c5b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 13 Apr 2022 13:06:17 +0200 Subject: [PATCH 034/245] preload - and and use `invokeWithRetry` (#146785) --- .../parts/sandbox/electron-browser/preload.js | 58 +++++++++++++++++-- .../electron-main/sharedProcess.ts | 6 +- .../electron-sandbox/sharedProcessService.ts | 4 +- test/automation/src/application.ts | 27 +-------- 4 files changed, 59 insertions(+), 36 deletions(-) diff --git a/src/vs/base/parts/sandbox/electron-browser/preload.js b/src/vs/base/parts/sandbox/electron-browser/preload.js index d8cc4c63220..3b74b9236e1 100644 --- a/src/vs/base/parts/sandbox/electron-browser/preload.js +++ b/src/vs/base/parts/sandbox/electron-browser/preload.js @@ -9,6 +9,10 @@ const { ipcRenderer, webFrame, contextBridge } = require('electron'); + /** + * @typedef {import('../common/sandboxTypes').ISandboxConfiguration} ISandboxConfiguration + */ + //#region Utilities /** @@ -49,14 +53,51 @@ return undefined; } + /** + * @param {string} channel + * @param {number} retryDelay + * @returns {Promise} + */ + async function invokeWithRetry(channel, retryDelay) { + let timeoutHandle; + + // A timeout promise that resolves after `retryDelay` + const timeout = new Promise(resolve => { + timeoutHandle = setTimeout(() => { + resolve(); + }, retryDelay); + }); + + // A first `invoke` call that clears the timeout + const firstInvoke = ((async () => { + try { + return await ipcRenderer.invoke(channel); + } finally { + clearTimeout(timeoutHandle); + } + })()); + + // Race the `invoke` to the `setTimeout` + const result = await Promise.race([ + firstInvoke, + timeout + ]); + + // If we have a result, return immediately + if (result) { + return result; + } + + console.warn(`[preload] ipcRenderer.invoke(${channel}) did not return after ${retryDelay}ms. Retrying once...`); + + // Otherwise, we retry once on the same channel + return ipcRenderer.invoke(channel); + } + //#endregion //#region Resolve Configuration - /** - * @typedef {import('../common/sandboxTypes').ISandboxConfiguration} ISandboxConfiguration - */ - /** @type {ISandboxConfiguration | undefined} */ let configuration = undefined; @@ -71,7 +112,14 @@ if (validateIPC(windowConfigIpcChannel)) { // Resolve configuration from electron-main - configuration = await ipcRenderer.invoke(windowConfigIpcChannel); + // + // TODO@electron there seems to be a condition where an early + // `ipcRenderer.invoke` call does not return when running in + // smoke tests where a debugger is attached. The workaround + // here is to retry the call, but the underlying reasons are + // not yet understood. + // (https://github.com/microsoft/vscode/issues/146785) + configuration = await invokeWithRetry(windowConfigIpcChannel, 5000); // Apply `userEnv` directly Object.assign(process.env, configuration.userEnv); diff --git a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts index 83f76d7f402..f7046a4d756 100644 --- a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts +++ b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts @@ -60,7 +60,7 @@ export class SharedProcess extends Disposable implements ISharedProcess { } private async onWindowConnection(e: IpcMainEvent, nonce: string): Promise { - this.logService.info('SharedProcess: on vscode:createSharedProcessMessageChannel'); + this.logService.trace('SharedProcess: on vscode:createSharedProcessMessageChannel'); // release barrier if this is the first window connection if (!this.firstWindowConnectionBarrier.isOpen()) { @@ -175,7 +175,7 @@ export class SharedProcess extends Disposable implements ISharedProcess { // Overall signal that the shared process window was loaded and // all services within have been created. this._whenReady = new Promise(resolve => validatedIpcMain.once('vscode:shared-process->electron-main=init-done', () => { - this.logService.info('SharedProcess: Overall ready'); + this.logService.trace('SharedProcess: Overall ready'); resolve(); })); @@ -200,7 +200,7 @@ export class SharedProcess extends Disposable implements ISharedProcess { // Wait for window indicating that IPC connections are accepted await new Promise(resolve => validatedIpcMain.once('vscode:shared-process->electron-main=ipc-ready', () => { - this.logService.info('SharedProcess: IPC ready'); + this.logService.trace('SharedProcess: IPC ready'); resolve(); })); diff --git a/src/vs/workbench/services/sharedProcess/electron-sandbox/sharedProcessService.ts b/src/vs/workbench/services/sharedProcess/electron-sandbox/sharedProcessService.ts index 2d2d4d50253..6590bc91938 100644 --- a/src/vs/workbench/services/sharedProcess/electron-sandbox/sharedProcessService.ts +++ b/src/vs/workbench/services/sharedProcess/electron-sandbox/sharedProcessService.ts @@ -42,10 +42,10 @@ export class SharedProcessService extends Disposable implements ISharedProcessSe // Acquire a message port connected to the shared process mark('code/willConnectSharedProcess'); - this.logService.info('Renderer->SharedProcess#connect: before acquirePort'); + this.logService.trace('Renderer->SharedProcess#connect: before acquirePort'); const port = await acquirePort('vscode:createSharedProcessMessageChannel', 'vscode:createSharedProcessMessageChannelResult'); mark('code/didConnectSharedProcess'); - this.logService.info('Renderer->SharedProcess#connect: connection established'); + this.logService.trace('Renderer->SharedProcess#connect: connection established'); return this._register(new MessagePortClient(port, `window:${this.windowId}`)); } diff --git a/test/automation/src/application.ts b/test/automation/src/application.ts index 23c9e630ea2..9220fa6e047 100644 --- a/test/automation/src/application.ts +++ b/test/automation/src/application.ts @@ -6,7 +6,6 @@ import { Workbench } from './workbench'; import { Code, launch, LaunchOptions } from './code'; import { Logger, measureAndLog } from './logger'; -import { PlaywrightDriver } from './playwrightDriver'; export const enum Quality { Dev, @@ -122,7 +121,7 @@ export class Application { await code.waitForWindowIds(ids => ids.length > 0); // We need a rendered workbench - await this.checkWorkbenchReady(code); + await measureAndLog(code.waitForElement('.monaco-workbench'), 'Application#checkWindowReady: wait for .monaco-workbench element', this.logger); // Remote but not web: wait for a remote connection state change if (this.remote) { @@ -141,28 +140,4 @@ export class Application { }, 300 /* = 30s of retry */), 'Application#checkWindowReady: wait for remote indicator', this.logger); } } - - private async checkWorkbenchReady(code: Code): Promise { - const driver = code.driver; - - // Web / Legacy: just poll for workbench element - if (this.web || !(driver instanceof PlaywrightDriver)) { - await measureAndLog(code.waitForElement('.monaco-workbench'), 'Application#checkWindowReady: wait for .monaco-workbench element', this.logger); - } - - // Desktop (playwright): we see hangs, where IPC messages - // are not delivered (https://github.com/microsoft/vscode/issues/146785) - // Workaround is to try to reload the window when that happens - else { - try { - await measureAndLog(code.waitForElement('.monaco-workbench', undefined, 100 /* 10s of retry */), 'Application#checkWindowReady: wait for .monaco-workbench element', this.logger); - } catch (error) { - this.logger.log(`checkWindowReady: giving up after 10s, reloading window and trying again...`); - - await driver.reload(); - - return this.checkWorkbenchReady(code); - } - } - } } From ae14caaf9fcdacff4ea45e70aa157f1f85a81ea5 Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 13 Apr 2022 13:45:13 +0200 Subject: [PATCH 035/245] remove setting, make hints always render compact, https://github.com/microsoft/vscode/issues/145191 --- src/vs/editor/common/config/editorOptions.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index febb7dac4d6..6ebe163d68a 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -2565,16 +2565,16 @@ class EditorInlayHints extends BaseEditorOption Date: Wed, 13 Apr 2022 14:46:18 +0200 Subject: [PATCH 036/245] disable codeql --- build/azure-pipelines/product-build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index f6eee5e5761..9c26e4a0fc4 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -129,6 +129,8 @@ variables: value: microsoft/vscode-distro - name: skipComponentGovernanceDetection value: true + - name: Codeql.SkipTaskAutoInjection + value: true resources: containers: From 55a30fcca81846c03e57f1201aa634c9336cc983 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 13 Apr 2022 14:59:03 +0200 Subject: [PATCH 037/245] More comment thread range polishing Part of #146510 --- .../contrib/comments/browser/commentColors.ts | 4 +- .../comments/browser/commentService.ts | 17 +++++++++ .../comments/browser/commentThreadBody.ts | 10 ++++- .../browser/commentThreadRangeDecorator.ts | 37 +++++++++++++++++-- .../comments/browser/commentThreadWidget.ts | 35 ++++++++++++++++++ .../browser/commentsEditorContribution.ts | 18 ++++++++- .../comments/browser/commentsTreeViewer.ts | 6 ++- .../contrib/comments/browser/media/panel.css | 5 +++ 8 files changed, 121 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentColors.ts b/src/vs/workbench/contrib/comments/browser/commentColors.ts index f906c411aad..78555e0e9e3 100644 --- a/src/vs/workbench/contrib/comments/browser/commentColors.ts +++ b/src/vs/workbench/contrib/comments/browser/commentColors.ts @@ -14,6 +14,8 @@ const resolvedCommentBorder = registerColor('editorCommentsWidget.resolvedBorder const unresolvedCommentBorder = registerColor('editorCommentsWidget.unresolvedBorder', { dark: peekViewBorder, light: peekViewBorder, hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('unresolvedCommentBorder', 'Color of borders and arrow for unresolved comments.')); export const commentThreadRangeBackground = registerColor('editorCommentsWidget.rangeBackground', { dark: transparent(unresolvedCommentBorder, .1), light: transparent(unresolvedCommentBorder, .1), hcDark: transparent(unresolvedCommentBorder, .1), hcLight: transparent(unresolvedCommentBorder, .1) }, nls.localize('commentThreadRangeBackground', 'Color of background for comment ranges.')); export const commentThreadRangeBorder = registerColor('editorCommentsWidget.rangeBorder', { dark: transparent(unresolvedCommentBorder, .4), light: transparent(unresolvedCommentBorder, .4), hcDark: transparent(unresolvedCommentBorder, .4), hcLight: transparent(unresolvedCommentBorder, .4) }, nls.localize('commentThreadRangeBackground', 'Color of background for comment ranges.')); +export const commentThreadRangeActiveBackground = registerColor('editorCommentsWidget.rangeActiveBackground', { dark: transparent(unresolvedCommentBorder, .1), light: transparent(unresolvedCommentBorder, .1), hcDark: transparent(unresolvedCommentBorder, .1), hcLight: transparent(unresolvedCommentBorder, .1) }, nls.localize('commentThreadRangeBackground', 'Color of background for comment ranges.')); +export const commentThreadRangeActiveBorder = registerColor('editorCommentsWidget.rangeActiveBorder', { dark: transparent(unresolvedCommentBorder, .4), light: transparent(unresolvedCommentBorder, .4), hcDark: transparent(unresolvedCommentBorder, .4), hcLight: transparent(unresolvedCommentBorder, .2) }, nls.localize('commentThreadRangeBackground', 'Color of background for comment ranges.')); const commentThreadStateColors = new Map([ [languages.CommentThreadState.Unresolved, unresolvedCommentBorder], @@ -23,8 +25,6 @@ const commentThreadStateColors = new Map([ export const commentThreadStateColorVar = '--comment-thread-state-color'; export const commentViewThreadStateColorVar = '--comment-view-thread-state-color'; export const commentThreadStateBackgroundColorVar = '--comment-thread-state-background-color'; -export const commentThreadRangeBackgroundColorVar = '--vscode-comment-thread-range-background'; -export const commentThreadRangeBorderColorVar = '--vscode-comment-thread-range-border'; export function getCommentThreadStateColor(state: languages.CommentThreadState | undefined, theme: IColorTheme): Color | undefined { const colorId = (state !== undefined) ? commentThreadStateColors.get(state) : undefined; diff --git a/src/vs/workbench/contrib/comments/browser/commentService.ts b/src/vs/workbench/contrib/comments/browser/commentService.ts index a11e4b8fb15..eed900b49eb 100644 --- a/src/vs/workbench/contrib/comments/browser/commentService.ts +++ b/src/vs/workbench/contrib/comments/browser/commentService.ts @@ -67,6 +67,7 @@ export interface ICommentService { readonly onDidUpdateCommentThreads: Event; readonly onDidUpdateNotebookCommentThreads: Event; readonly onDidChangeActiveCommentThread: Event; + readonly onDidChangeCurrentCommentThread: Event; readonly onDidUpdateCommentingRanges: Event<{ owner: string }>; readonly onDidChangeActiveCommentingRange: Event<{ range: Range; commentingRangesInfo: CommentingRanges }>; readonly onDidSetDataProvider: Event; @@ -90,6 +91,7 @@ export interface ICommentService { hasReactionHandler(owner: string): boolean; toggleReaction(owner: string, resource: URI, thread: CommentThread, comment: Comment, reaction: CommentReaction): Promise; setActiveCommentThread(commentThread: CommentThread | null): void; + setCurrentCommentThread(commentThread: CommentThread | undefined): void; } export class CommentService extends Disposable implements ICommentService { @@ -119,6 +121,9 @@ export class CommentService extends Disposable implements ICommentService { private readonly _onDidChangeActiveCommentThread = this._register(new Emitter()); readonly onDidChangeActiveCommentThread = this._onDidChangeActiveCommentThread.event; + private readonly _onDidChangeCurrentCommentThread = this._register(new Emitter()); + readonly onDidChangeCurrentCommentThread = this._onDidChangeCurrentCommentThread.event; + private readonly _onDidChangeActiveCommentingRange: Emitter<{ range: Range; commentingRangesInfo: CommentingRanges; @@ -137,6 +142,18 @@ export class CommentService extends Disposable implements ICommentService { super(); } + /** + * The current comment thread is the thread that has focus or is being hovered. + * @param commentThread + */ + setCurrentCommentThread(commentThread: CommentThread | undefined) { + this._onDidChangeCurrentCommentThread.fire(commentThread); + } + + /** + * The active comment thread is the the thread that is currently being edited. + * @param commentThread + */ setActiveCommentThread(commentThread: CommentThread | null) { this._onDidChangeActiveCommentThread.fire(commentThread); } diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadBody.ts b/src/vs/workbench/contrib/comments/browser/commentThreadBody.ts index fca40378daa..59a1d57b12b 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadBody.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadBody.ts @@ -209,8 +209,14 @@ export class CommentThreadBody extends D } private _updateAriaLabel() { - this._commentsElement.ariaLabel = nls.localize('commentThreadAria', "Comment thread with {0} comments. {1}.", - this._commentThread.comments?.length, this._commentThread.label); + if (this._commentThread.isDocumentCommentThread()) { + this._commentsElement.ariaLabel = nls.localize('commentThreadAria.withRange', "Comment thread with {0} comments on lines {1} through {2}. {3}.", + this._commentThread.comments?.length, this._commentThread.range.startLineNumber, this._commentThread.range.endLineNumber, + this._commentThread.label); + } else { + this._commentsElement.ariaLabel = nls.localize('commentThreadAria', "Comment thread with {0} comments. {1}.", + this._commentThread.comments?.length, this._commentThread.label); + } } private _setFocusedComment(value: number | undefined) { diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadRangeDecorator.ts b/src/vs/workbench/contrib/comments/browser/commentThreadRangeDecorator.ts index adadb4a6617..5af04a9fe0c 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadRangeDecorator.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadRangeDecorator.ts @@ -3,11 +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 { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IRange } from 'vs/editor/common/core/range'; import { IModelDecorationOptions, IModelDeltaDecoration } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -import { ICommentInfo } from 'vs/workbench/contrib/comments/browser/commentService'; +import { ICommentInfo, ICommentService } from 'vs/workbench/contrib/comments/browser/commentService'; class CommentThreadRangeDecoration implements IModelDeltaDecoration { private _decorationId: string | undefined; @@ -26,12 +27,16 @@ class CommentThreadRangeDecoration implements IModelDeltaDecoration { } } -export class CommentThreadRangeDecorator { - public static description = 'comment-thread-range-decorator'; +export class CommentThreadRangeDecorator extends Disposable { + private static description = 'comment-thread-range-decorator'; private decorationOptions: ModelDecorationOptions; + private activeDecorationOptions: ModelDecorationOptions; private decorationIds: string[] = []; + private activeDecorationIds: string[] = []; + private editor: ICodeEditor | undefined; - constructor() { + constructor(commentService: ICommentService) { + super(); const decorationOptions: IModelDecorationOptions = { description: CommentThreadRangeDecorator.description, isWholeLine: false, @@ -40,6 +45,29 @@ export class CommentThreadRangeDecorator { }; this.decorationOptions = ModelDecorationOptions.createDynamic(decorationOptions); + + const activeDecorationOptions: IModelDecorationOptions = { + description: CommentThreadRangeDecorator.description, + isWholeLine: false, + zIndex: 20, + className: 'comment-thread-range-current' + }; + + this.activeDecorationOptions = ModelDecorationOptions.createDynamic(activeDecorationOptions); + this._register(commentService.onDidChangeCurrentCommentThread(thread => { + if (!this.editor) { + return; + } + let newDecoration: CommentThreadRangeDecoration[] = []; + if (thread) { + const range = thread.range; + if (!((range.startLineNumber === range.endLineNumber) && (range.startColumn === range.endColumn))) { + newDecoration.push(new CommentThreadRangeDecoration(range, this.activeDecorationOptions)); + } + } + this.activeDecorationIds = this.editor.deltaDecorations(this.activeDecorationIds, newDecoration); + newDecoration.forEach((decoration, index) => decoration.id = this.decorationIds[index]); + })); } public update(editor: ICodeEditor, commentInfos: ICommentInfo[]) { @@ -47,6 +75,7 @@ export class CommentThreadRangeDecorator { if (!model) { return; } + this.editor = editor; const commentThreadRangeDecorations: CommentThreadRangeDecoration[] = []; for (const info of commentInfos) { diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts index 0f9f5fd1500..92b06773800 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts @@ -109,6 +109,41 @@ export class CommentThreadWidget extends if (controller) { commentControllerKey.set(controller.contextValue); } + + this.currentThreadListeners(); + } + + private updateCurrentThread(hasMouse: boolean, hasFocus: boolean) { + if (hasMouse || hasFocus) { + this.commentService.setCurrentCommentThread(this.commentThread); + } else { + this.commentService.setCurrentCommentThread(undefined); + } + } + + private currentThreadListeners() { + let hasMouse = false; + let hasFocus = false; + this._register(dom.addDisposableListener(this.container, dom.EventType.MOUSE_ENTER, (e) => { + if ((e).toElement === this.container) { + hasMouse = true; + this.updateCurrentThread(hasMouse, hasFocus); + } + }, true)); + this._register(dom.addDisposableListener(this.container, dom.EventType.MOUSE_LEAVE, (e) => { + if ((e).fromElement === this.container) { + hasMouse = false; + this.updateCurrentThread(hasMouse, hasFocus); + } + }, true)); + this._register(dom.addDisposableListener(this.container, dom.EventType.FOCUS_IN, () => { + hasFocus = true; + this.updateCurrentThread(hasMouse, hasFocus); + }, true)); + this._register(dom.addDisposableListener(this.container, dom.EventType.FOCUS_OUT, () => { + hasFocus = false; + this.updateCurrentThread(hasMouse, hasFocus); + }, true)); } updateCommentThread(commentThread: languages.CommentThread) { diff --git a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts index 14f1f730236..5d80b0e5dfd 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts @@ -47,7 +47,7 @@ import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/cont import { Position } from 'vs/editor/common/core/position'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { CommentThreadRangeDecorator } from 'vs/workbench/contrib/comments/browser/commentThreadRangeDecorator'; -import { commentThreadRangeBackground, commentThreadRangeBorder } from 'vs/workbench/contrib/comments/browser/commentColors'; +import { commentThreadRangeActiveBackground, commentThreadRangeActiveBorder, commentThreadRangeBackground, commentThreadRangeBorder } from 'vs/workbench/contrib/comments/browser/commentColors'; import { ICursorSelectionChangedEvent } from 'vs/editor/common/cursorEvents'; import { CommentsPanel } from 'vs/workbench/contrib/comments/browser/commentsView'; @@ -343,7 +343,7 @@ export class CommentController implements IEditorContribution { } })); - this._commentThreadRangeDecorator = new CommentThreadRangeDecorator(); + this.globalToDispose.add(this._commentThreadRangeDecorator = new CommentThreadRangeDecorator(this.commentService)); this.globalToDispose.add(this.commentService.onDidDeleteDataProvider(ownerId => { delete this._pendingCommentCache[ownerId]; @@ -1104,4 +1104,18 @@ registerThemingParticipant((theme, collector) => { border-style: solid; box-sizing: border-box; }`); } + + const commentThreadRangeActiveBackgroundColor = theme.getColor(commentThreadRangeActiveBackground); + if (commentThreadRangeActiveBackgroundColor) { + collector.addRule(`.monaco-editor .comment-thread-range-current { background-color: ${commentThreadRangeActiveBackgroundColor};}`); + } + + const commentThreadRangeActiveBorderColor = theme.getColor(commentThreadRangeActiveBorder); + if (commentThreadRangeActiveBorderColor) { + collector.addRule(`.monaco-editor .comment-thread-range-current { + border-color: ${commentThreadRangeActiveBorderColor}; + border-width: 1px; + border-style: solid; + box-sizing: border-box; }`); + } }); diff --git a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts index ebe8b7b9de2..a65cffe56f7 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts @@ -58,6 +58,7 @@ interface ICommentThreadTemplateData { timestamp: TimestampWidget; separator: HTMLElement; commentPreview: HTMLSpanElement; + range: HTMLSpanElement; }; repliesMetadata: { container: HTMLElement; @@ -138,7 +139,8 @@ export class CommentNodeRenderer implements IListRenderer userNames: dom.append(metadataContainer, dom.$('.user')), timestamp: new TimestampWidget(this.configurationService, dom.append(metadataContainer, dom.$('.timestamp-container'))), separator: dom.append(metadataContainer, dom.$('.separator')), - commentPreview: dom.append(metadataContainer, dom.$('.text')) + commentPreview: dom.append(metadataContainer, dom.$('.text')), + range: dom.append(metadataContainer, dom.$('.range')) }; data.threadMetadata.separator.innerText = '\u00b7'; @@ -210,6 +212,8 @@ export class CommentNodeRenderer implements IListRenderer templateData.threadMetadata.commentPreview.title = renderedComment.element.textContent ?? ''; } + templateData.threadMetadata.range.textContent = `[${node.element.range.startLineNumber}-${node.element.range.endLineNumber}]`; + if (!node.element.hasReply()) { templateData.repliesMetadata.container.style.display = 'none'; return; diff --git a/src/vs/workbench/contrib/comments/browser/media/panel.css b/src/vs/workbench/contrib/comments/browser/media/panel.css index 8b2e685cb8b..0775ef6a1aa 100644 --- a/src/vs/workbench/contrib/comments/browser/media/panel.css +++ b/src/vs/workbench/contrib/comments/browser/media/panel.css @@ -74,6 +74,11 @@ text-overflow: ellipsis; max-width: 500px; overflow: hidden; + padding-right: 5px; +} + +.comments-panel .comments-panel-container .tree-container .comment-thread-container .range { + opacity: 0.8; } .comments-panel .comments-panel-container .tree-container .comment-thread-container .comment-snippet-container .text code { From 4f06eb2fe74adea289a07b7ff7b15135e9b828d6 Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 13 Apr 2022 15:39:10 +0200 Subject: [PATCH 038/245] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f2996e6d45b..1106daf4c62 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.67.0", - "distro": "5589eefba75be18bb4b240d9e45efd88efd782e9", + "distro": "5c715d21e609df4020231e89b9a0a684dcfa6ffd", "author": { "name": "Microsoft Corporation" }, From 340efad49570e01e4f17c78c804dc8b6933c5a42 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 13 Apr 2022 16:05:28 +0200 Subject: [PATCH 039/245] SCM - Source Control Repositories view sort setting (#147001) --- .../contrib/scm/browser/scm.contribution.ts | 11 + .../contrib/scm/browser/scmViewService.ts | 276 +++++++++++------- 2 files changed, 186 insertions(+), 101 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index 2b5f202e350..75866cdf644 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -259,6 +259,17 @@ Registry.as(ConfigurationExtensions.Configuration).regis markdownDescription: localize('alwaysShowRepository', "Controls whether repositories should always be visible in the Source Control view."), default: false }, + 'scm.repositories.sortOrder': { + type: 'string', + enum: ['discovery time', 'name', 'path'], + enumDescriptions: [ + localize('scm.repositoriesSortOrder.discoveryTime', "Repositories in the Source Control Repositories view are sorted by discovery time. Repositories in the Source Control view are sorted in the order that they were selected."), + localize('scm.repositoriesSortOrder.name', "Repositories in the Source Control Repositories and Source Control views are sorted by repository name."), + localize('scm.repositoriesSortOrder.path', "Repositories in the Source Control Repositories and Source Control views are sorted by repository path.") + ], + description: localize('repositoriesSortOrder', "Controls the sort order of the repositories in the source control repositories view."), + default: 'discovery time' + }, 'scm.repositories.visible': { type: 'number', description: localize('providersVisible', "Controls how many repositories are visible in the Source Control Repositories section. Set to `0` to be able to manually resize the view."), diff --git a/src/vs/workbench/contrib/scm/browser/scmViewService.ts b/src/vs/workbench/contrib/scm/browser/scmViewService.ts index 757c5526c7a..caba59981f3 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewService.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewService.ts @@ -15,6 +15,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { compareFileNames, comparePaths } from 'vs/base/common/comparers'; import { basename } from 'vs/base/common/resources'; import { binarySearch } from 'vs/base/common/arrays'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; function getProviderStorageKey(provider: ISCMProvider): string { return `${provider.contextValue}:${provider.label}${provider.rootUri ? `:${provider.rootUri.toString()}` : ''}`; @@ -29,8 +30,18 @@ function getRepositoryName(workspaceContextService: IWorkspaceContextService, re return folder?.uri.toString() === repository.provider.rootUri.toString() ? folder.name : basename(repository.provider.rootUri); } +type ISCMRepositoryViewSortKey = 'discovery time' | 'name' | 'path'; + +interface ISCMRepositoryView { + readonly repository: ISCMRepository; + readonly discoveryTime: number; + focused: boolean; + selectionIndex: number; +} + export interface ISCMViewServiceState { readonly all: string[]; + readonly sortKey: ISCMRepositoryViewSortKey; readonly visible: number[]; } @@ -45,20 +56,24 @@ export class SCMViewService implements ISCMViewService { private previousState: ISCMViewServiceState | undefined; private disposables = new DisposableStore(); - private _repositories: ISCMRepository[] = []; + private _repositories: ISCMRepositoryView[] = []; get repositories(): ISCMRepository[] { - return this._repositories; + return this._repositories.map(r => r.repository); } - private _onDidChangeRepositories = new Emitter(); - readonly onDidChangeRepositories = this._onDidChangeRepositories.event; - - private _visibleRepositoriesSet = new Set(); - private _visibleRepositories: ISCMRepository[] = []; - get visibleRepositories(): ISCMRepository[] { - return this._visibleRepositories; + // In order to match the legacy behaviour, when the repositories are sorted by discovery time, + // the visible repositories are sorted by the selection index instead of the discovery time. + if (this._repositoriesSortKey === 'discovery time') { + return this._repositories.filter(r => r.selectionIndex !== -1) + .sort((r1, r2) => r1.selectionIndex - r2.selectionIndex) + .map(r => r.repository); + } + + return this._repositories + .filter(r => r.selectionIndex !== -1) + .map(r => r.repository); } set visibleRepositories(visibleRepositories: ISCMRepository[]) { @@ -66,15 +81,18 @@ export class SCMViewService implements ISCMViewService { const added = new Set(); const removed = new Set(); - for (const repository of visibleRepositories) { - if (!this._visibleRepositoriesSet.has(repository)) { - added.add(repository); + for (const repositoryView of this._repositories) { + // Selected -> !Selected + if (!set.has(repositoryView.repository) && repositoryView.selectionIndex !== -1) { + repositoryView.selectionIndex = -1; + removed.add(repositoryView.repository); } - } - - for (const repository of this._visibleRepositories) { - if (!set.has(repository)) { - removed.add(repository); + // Selected | !Selected -> Selected + if (set.has(repositoryView.repository)) { + if (repositoryView.selectionIndex === -1) { + added.add(repositoryView.repository); + } + repositoryView.selectionIndex = visibleRepositories.indexOf(repositoryView.repository); } } @@ -82,15 +100,17 @@ export class SCMViewService implements ISCMViewService { return; } - this._visibleRepositories = visibleRepositories.sort(this._compareRepositories); - this._visibleRepositoriesSet = set; this._onDidSetVisibleRepositories.fire({ added, removed }); - if (this._focusedRepository && removed.has(this._focusedRepository)) { - this.focus(this._visibleRepositories[0]); + // Update focus if the focused repository is not visible anymore + if (this._repositories.find(r => r.focused && r.selectionIndex === -1)) { + this.focus(this._repositories.find(r => r.selectionIndex !== -1)?.repository); } } + private _onDidChangeRepositories = new Emitter(); + readonly onDidChangeRepositories = this._onDidChangeRepositories.event; + private _onDidSetVisibleRepositories = new Emitter(); readonly onDidChangeVisibleRepositories = Event.any( this._onDidSetVisibleRepositories.event, @@ -101,53 +121,66 @@ export class SCMViewService implements ISCMViewService { return e; } - return { - added: Iterable.concat(last.added, e.added), - removed: Iterable.concat(last.removed, e.removed), - }; + const added = new Set(last.added); + const removed = new Set(last.removed); + + for (const repository of e.added) { + if (removed.has(repository)) { + removed.delete(repository); + } else { + added.add(repository); + } + } + for (const repository of e.removed) { + if (added.has(repository)) { + added.delete(repository); + } else { + removed.add(repository); + } + } + + return { added, removed }; }, 0) ); - private _focusedRepository: ISCMRepository | undefined; - get focusedRepository(): ISCMRepository | undefined { - return this._focusedRepository; + return this._repositories.find(r => r.focused)?.repository; } private _onDidFocusRepository = new Emitter(); readonly onDidFocusRepository = this._onDidFocusRepository.event; - private _compareRepositories: (op1: ISCMRepository, op2: ISCMRepository) => number; + private _repositoriesSortKey: ISCMRepositoryViewSortKey; constructor( - @ISCMService private readonly scmService: ISCMService, + @ISCMService scmService: ISCMService, @IInstantiationService instantiationService: IInstantiationService, + @IConfigurationService private readonly configurationService: IConfigurationService, @IStorageService private readonly storageService: IStorageService, - @IWorkspaceContextService workspaceContextService: IWorkspaceContextService + @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService ) { this.menus = instantiationService.createInstance(SCMMenus); - this._compareRepositories = (op1: ISCMRepository, op2: ISCMRepository): number => { - const name1 = getRepositoryName(workspaceContextService, op1); - const name2 = getRepositoryName(workspaceContextService, op2); - - const nameComparison = compareFileNames(name1, name2); - if (nameComparison === 0 && op1.provider.rootUri && op2.provider.rootUri) { - return comparePaths(op1.provider.rootUri.fsPath, op2.provider.rootUri.fsPath); - } - - return nameComparison; - }; - - scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables); - scmService.onDidRemoveRepository(this.onDidRemoveRepository, this, this.disposables); - try { this.previousState = JSON.parse(storageService.get('scm:view:visibleRepositories', StorageScope.WORKSPACE, '')); } catch { // noop } + this._repositoriesSortKey = this.previousState?.sortKey ?? this.getViewSortOrder(); + + scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables); + scmService.onDidRemoveRepository(this.onDidRemoveRepository, this, this.disposables); + + configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('scm.repositories.sortOrder')) { + this._repositoriesSortKey = this.getViewSortOrder(); + this._repositories.sort(this.compareRepositories.bind(this)); + + this._onDidChangeRepositories.fire({ added: Iterable.empty(), removed: Iterable.empty() }); + } + }); + for (const repository of scmService.repositories) { this.onDidAddRepository(repository); } @@ -160,50 +193,61 @@ export class SCMViewService implements ISCMViewService { this.eventuallyFinishLoading(); } - this.insertRepository(this._repositories, repository); + const repositoryView: ISCMRepositoryView = { + repository, discoveryTime: Date.now(), focused: false, selectionIndex: -1 + }; + let removed: Iterable = Iterable.empty(); if (this.previousState) { const index = this.previousState.all.indexOf(getProviderStorageKey(repository.provider)); - if (index === -1) { // saw a repo we did not expect + if (index === -1) { + // This repository is not part of the previous state which means that it + // was either manually closed in the previous session, or the repository + // was added after the previous session.In this case, we should select all + // of the repositories. const added: ISCMRepository[] = []; - for (const repo of this.scmService.repositories) { // all should be visible - if (!this._visibleRepositoriesSet.has(repo)) { - added.push(repository); - } - } - this._visibleRepositoriesSet = new Set(this.scmService.repositories); - this._visibleRepositories = [...this.scmService.repositories.sort(this._compareRepositories)]; + this.insertRepositoryView(this._repositories, repositoryView); + this._repositories.forEach((repositoryView, index) => { + if (repositoryView.selectionIndex === -1) { + added.push(repositoryView.repository); + } + repositoryView.selectionIndex = index; + }); + this._onDidChangeRepositories.fire({ added, removed: Iterable.empty() }); - this.finishLoading(); + this.didSelectRepository = false; return; } - if (this.previousState.visible.indexOf(index) > -1) { - // First visible repository - if (!this.didSelectRepository) { - removed = this._visibleRepositories; - - this._visibleRepositories = []; - this._visibleRepositoriesSet = new Set(); - this.didSelectRepository = true; - } - } else { + if (this.previousState.visible.indexOf(index) === -1) { // Explicit selection started if (this.didSelectRepository) { + this.insertRepositoryView(this._repositories, repositoryView); this._onDidChangeRepositories.fire({ added: Iterable.empty(), removed: Iterable.empty() }); return; } + } else { + // First visible repository + if (!this.didSelectRepository) { + removed = [...this.visibleRepositories]; + this._repositories.forEach(r => { + r.focused = false; + r.selectionIndex = -1; + }); + + this.didSelectRepository = true; + } } } - this._visibleRepositoriesSet.add(repository); - this.insertRepository(this._visibleRepositories, repository); - this._onDidChangeRepositories.fire({ added: [repository], removed }); + const maxSelectionIndex = this.getMaxSelectionIndex(); + this.insertRepositoryView(this._repositories, { ...repositoryView, selectionIndex: maxSelectionIndex + 1 }); + this._onDidChangeRepositories.fire({ added: [repositoryView.repository], removed }); - if (!this._focusedRepository) { + if (!this._repositories.find(r => r.focused)) { this.focus(repository); } } @@ -213,39 +257,29 @@ export class SCMViewService implements ISCMViewService { this.eventuallyFinishLoading(); } + const repositoriesIndex = this._repositories.findIndex(r => r.repository === repository); + + if (repositoriesIndex === -1) { + return; + } + let added: Iterable = Iterable.empty(); + const repositoryView = this._repositories.splice(repositoriesIndex, 1); - const repositoriesIndex = this._repositories.indexOf(repository); - const visibleRepositoriesIndex = this._visibleRepositories.indexOf(repository); - - if (repositoriesIndex > -1) { - this._repositories.splice(repositoriesIndex, 1); + if (this._repositories.length > 0 && this.visibleRepositories.length === 0) { + this._repositories[0].selectionIndex = 0; + added = [this._repositories[0].repository]; } - if (visibleRepositoriesIndex > -1) { - this._visibleRepositories.splice(visibleRepositoriesIndex, 1); - this._visibleRepositoriesSet.delete(repository); + this._onDidChangeRepositories.fire({ added, removed: repositoryView.map(r => r.repository) }); - if (this._repositories.length > 0 && this._visibleRepositories.length === 0) { - const first = this._repositories[0]; - - this._visibleRepositories.push(first); - this._visibleRepositoriesSet.add(first); - added = [first]; - } - } - - if (repositoriesIndex > -1 || visibleRepositoriesIndex > -1) { - this._onDidChangeRepositories.fire({ added, removed: [repository] }); - } - - if (this._focusedRepository === repository) { - this.focus(this._visibleRepositories[0]); + if (repositoryView.length === 1 && repositoryView[0].focused && this.visibleRepositories.length > 0) { + this.focus(this.visibleRepositories[0]); } } isVisible(repository: ISCMRepository): boolean { - return this._visibleRepositoriesSet.has(repository); + return this._repositories.find(r => r.repository === repository)?.selectionIndex !== -1; } toggleVisibility(repository: ISCMRepository, visible?: boolean): void { @@ -270,17 +304,57 @@ export class SCMViewService implements ISCMViewService { } focus(repository: ISCMRepository | undefined): void { - if (repository && !this.visibleRepositories.includes(repository)) { + if (repository && !this.isVisible(repository)) { return; } - this._focusedRepository = repository; - this._onDidFocusRepository.fire(repository); + this._repositories.forEach(r => r.focused = r.repository === repository); + + if (this._repositories.find(r => r.focused)) { + this._onDidFocusRepository.fire(repository); + } } - private insertRepository(repositories: ISCMRepository[], repository: ISCMRepository): void { - const index = binarySearch(repositories, repository, this._compareRepositories); - repositories.splice(index < 0 ? ~index : index, 0, repository); + private compareRepositories(op1: ISCMRepositoryView, op2: ISCMRepositoryView): number { + // Sort by discovery time + if (this._repositoriesSortKey === 'discovery time') { + return op1.discoveryTime - op2.discoveryTime; + } + + // Sort by path + if (this._repositoriesSortKey === 'path' && op1.repository.provider.rootUri && op2.repository.provider.rootUri) { + return comparePaths(op1.repository.provider.rootUri.fsPath, op2.repository.provider.rootUri.fsPath); + } + + // Sort by name, path + const name1 = getRepositoryName(this.workspaceContextService, op1.repository); + const name2 = getRepositoryName(this.workspaceContextService, op2.repository); + + const nameComparison = compareFileNames(name1, name2); + if (nameComparison === 0 && op1.repository.provider.rootUri && op2.repository.provider.rootUri) { + return comparePaths(op1.repository.provider.rootUri.fsPath, op2.repository.provider.rootUri.fsPath); + } + + return nameComparison; + } + + private getMaxSelectionIndex(): number { + return this._repositories.length === 0 ? -1 : + Math.max(...this._repositories.map(r => r.selectionIndex)); + } + + private getViewSortOrder(): ISCMRepositoryViewSortKey { + const sortOder = this.configurationService.getValue('scm.repositories.sortOrder'); + if (sortOder !== 'discovery time' && sortOder !== 'name' && sortOder !== 'path') { + return 'discovery time'; + } + + return sortOder; + } + + private insertRepositoryView(repositories: ISCMRepositoryView[], repositoryView: ISCMRepositoryView): void { + const index = binarySearch(repositories, repositoryView, this.compareRepositories.bind(this)); + repositories.splice(index < 0 ? ~index : index, 0, repositoryView); } private onWillSaveState(): void { @@ -290,7 +364,7 @@ export class SCMViewService implements ISCMViewService { const all = this.repositories.map(r => getProviderStorageKey(r.provider)); const visible = this.visibleRepositories.map(r => all.indexOf(getProviderStorageKey(r.provider))); - const raw = JSON.stringify({ all, visible }); + const raw = JSON.stringify({ all, sortKey: this._repositoriesSortKey, visible }); this.storageService.store('scm:view:visibleRepositories', raw, StorageScope.WORKSPACE, StorageTarget.MACHINE); } From 2aa78c8cfb9005f756ccf432b076b5ff1d304a8f Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 13 Apr 2022 16:07:06 +0200 Subject: [PATCH 040/245] bump gulp-shell --- build/package.json | 1 + build/yarn.lock | 122 +++++++++++++++++++++++++++++++++++++++++++-- package.json | 1 - yarn.lock | 44 +--------------- 4 files changed, 122 insertions(+), 46 deletions(-) diff --git a/build/package.json b/build/package.json index 3403f5c5c13..ab6b24d496d 100644 --- a/build/package.json +++ b/build/package.json @@ -54,6 +54,7 @@ "fs-extra": "^9.1.0", "got": "11.8.1", "gulp-merge-json": "^2.1.1", + "gulp-shell": "^0.8.0", "jsonc-parser": "^2.3.0", "mime": "^1.4.1", "mkdirp": "^1.0.4", diff --git a/build/yarn.lock b/build/yarn.lock index 9b91f75047d..db78aeacc16 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -798,6 +798,13 @@ ansi-colors@^1.0.1: dependencies: ansi-wrap "^0.1.0" +ansi-gray@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251" + integrity sha1-KWLPVOyXksSFEKPetSRDaGHvclE= + dependencies: + ansi-wrap "0.1.0" + ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -805,7 +812,14 @@ ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" -ansi-wrap@^0.1.0: +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-wrap@0.1.0, ansi-wrap@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768= @@ -1033,6 +1047,14 @@ chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + cheerio-select@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.5.0.tgz#faf3daeb31b17c5e1a9dabcee288aaf8aafa5823" @@ -1109,11 +1131,28 @@ color-convert@^1.9.0: dependencies: color-name "1.1.3" +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-support@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + colors@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" @@ -1592,6 +1631,16 @@ extract-zip@^2.0.1: optionalDependencies: "@types/yauzl" "^2.9.1" +fancy-log@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.3.tgz#dbc19154f558690150a23953a0adbd035be45fc7" + integrity sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw== + dependencies: + ansi-gray "^0.1.1" + color-support "^1.1.3" + parse-node-version "^1.0.0" + time-stamp "^1.0.0" + fast-glob@^3.2.9: version "3.2.11" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" @@ -1832,11 +1881,28 @@ gulp-merge-json@^2.1.1: through "^2.3.8" vinyl "^2.1.0" +gulp-shell@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/gulp-shell/-/gulp-shell-0.8.0.tgz#0ed4980de1d0c67e5f6cce971d7201fd0be50555" + integrity sha512-wHNCgmqbWkk1c6Gc2dOL5SprcoeujQdeepICwfQRo91DIylTE7a794VEE+leq3cE2YDoiS5ulvRfKVIEMazcTQ== + dependencies: + chalk "^3.0.0" + fancy-log "^1.3.3" + lodash.template "^4.5.0" + plugin-error "^1.0.1" + through2 "^3.0.1" + tslib "^1.10.0" + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + has-symbols@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" @@ -1909,7 +1975,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -2115,6 +2181,11 @@ linkify-it@^2.0.0: dependencies: uc.micro "^1.0.1" +lodash._reinterpolate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= + lodash.includes@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" @@ -2155,6 +2226,21 @@ lodash.once@^4.0.0: resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= +lodash.template@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" + integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== + dependencies: + lodash._reinterpolate "^3.0.0" + lodash.templatesettings "^4.0.0" + +lodash.templatesettings@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" + integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== + dependencies: + lodash._reinterpolate "^3.0.0" + lodash@^4.17.10, lodash@^4.17.15: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -2380,6 +2466,11 @@ p-limit@*, p-limit@^3.1.0: dependencies: yocto-queue "^0.1.0" +parse-node-version@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" + integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== + parse-semver@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/parse-semver/-/parse-semver-1.1.1.tgz#9a4afd6df063dc4826f93fba4a99cf223f666cb8" @@ -2533,7 +2624,7 @@ read@^1.0.7: dependencies: mute-stream "~0.0.4" -readable-stream@3: +"readable-stream@2 || 3", readable-stream@3: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -2772,6 +2863,21 @@ supports-color@^6.1.0: dependencies: has-flag "^3.0.0" +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +through2@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.2.tgz#99f88931cfc761ec7678b41d5d7336b5b6a07bf4" + integrity sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ== + dependencies: + inherits "^2.0.4" + readable-stream "2 || 3" + through2@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/through2/-/through2-4.0.2.tgz#a7ce3ac2a7a8b0b966c80e7c49f0484c3b239764" @@ -2784,6 +2890,11 @@ through@^2.3.8: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= +time-stamp@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" + integrity sha1-dkpaEa9QVhkhsTPztE5hhofg9cM= + tmp@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" @@ -2817,6 +2928,11 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= +tslib@^1.10.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + tslib@^1.8.1: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" diff --git a/package.json b/package.json index 1106daf4c62..686ad423d90 100644 --- a/package.json +++ b/package.json @@ -161,7 +161,6 @@ "gulp-remote-retry-src": "^0.8.0", "gulp-rename": "^1.2.0", "gulp-replace": "^0.5.4", - "gulp-shell": "^0.6.5", "gulp-sourcemaps": "^3.0.0", "gulp-svgmin": "^4.1.0", "gulp-tsb": "4.0.6", diff --git a/yarn.lock b/yarn.lock index 782aba6424d..111eae22516 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2434,13 +2434,6 @@ async-settle@^1.0.0: dependencies: async-done "^1.2.2" -async@^2.1.5: - version "2.6.3" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" - integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== - dependencies: - lodash "^4.17.14" - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -2980,7 +2973,7 @@ chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -5954,19 +5947,6 @@ gulp-replace@^0.5.4: readable-stream "^2.0.1" replacestream "^4.0.0" -gulp-shell@^0.6.5: - version "0.6.5" - resolved "https://registry.yarnpkg.com/gulp-shell/-/gulp-shell-0.6.5.tgz#f07b204ad8ad1c2659f7a1b6d76efa16d416a759" - integrity sha512-f3m1WcS0o2B72/PGj1Jbv9zYR9rynBh/EQJv64n01xQUo7j7anols0eww9GG/WtDTzGVQLrupVDYkifRFnj5Zg== - dependencies: - async "^2.1.5" - chalk "^2.3.0" - fancy-log "^1.3.2" - lodash "^4.17.4" - lodash.template "^4.4.0" - plugin-error "^0.1.2" - through2 "^2.0.3" - gulp-sourcemaps@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/gulp-sourcemaps/-/gulp-sourcemaps-3.0.0.tgz#2e154e1a2efed033c0e48013969e6f30337b2743" @@ -7453,11 +7433,6 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" -lodash._reinterpolate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" - integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= - lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" @@ -7493,27 +7468,12 @@ lodash.some@^4.2.2: resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d" integrity sha1-G7nzFO9ri63tE7VJFpsqlF62jk0= -lodash.template@^4.4.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" - integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.templatesettings "^4.0.0" - -lodash.templatesettings@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" - integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4: +lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== From 44b6695d4e1cf838107fc3d0cf55e9055370d4ad Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 13 Apr 2022 17:10:28 +0200 Subject: [PATCH 041/245] timeline - add setting to bring back "Uncommitted Changes" (#147372) --- extensions/git/package.json | 6 ++++++ extensions/git/package.nls.json | 1 + extensions/git/src/timelineProvider.ts | 28 +++++++++++++++++++++++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 1122b41fc4c..117cc1b0761 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -2222,6 +2222,12 @@ "description": "%config.timeline.showAuthor%", "scope": "window" }, + "git.timeline.showUncommitted": { + "type": "boolean", + "default": false, + "description": "%config.timeline.showUncommitted%", + "scope": "window" + }, "git.showUnpublishedCommitsButton": { "type": "string", "enum": [ diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 6675bd12718..b19611f7dd9 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -188,6 +188,7 @@ "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.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.", "config.timeline.date.committed": "Use the committed date", "config.timeline.date.authored": "Use the authored date", diff --git a/extensions/git/src/timelineProvider.ts b/extensions/git/src/timelineProvider.ts index f2e665eaafc..8d65812a8d0 100644 --- a/extensions/git/src/timelineProvider.ts +++ b/extensions/git/src/timelineProvider.ts @@ -169,6 +169,8 @@ export class GitTimelineProvider implements TimelineProvider { const dateType = config.get<'committed' | 'authored'>('date'); const showAuthor = config.get('showAuthor'); + const showUncommitted = config.get('showUncommitted'); + const openComparison = localize('git.timeline.openComparison', "Open Comparison"); const items = commits.map((c, i) => { @@ -220,6 +222,30 @@ export class GitTimelineProvider implements TimelineProvider { items.splice(0, 0, item); } + + if (showUncommitted) { + const working = repo.workingTreeGroup.resourceStates.find(r => r.resourceUri.fsPath === uri.fsPath); + if (working) { + const date = new Date(); + + const item = new GitTimelineItem('', index ? '~' : 'HEAD', localize('git.timeline.uncommitedChanges', 'Uncommitted Changes'), date.getTime(), 'working', 'git:file:working'); + // TODO@eamodio: Replace with a better icon -- reflecting its status maybe? + item.iconPath = new ThemeIcon('git-commit'); + item.description = ''; + item.setItemDetails(you, undefined, dateFormatter.format(date), Resource.getStatusText(working.type)); + + const cmd = this.commands.resolveTimelineOpenDiffCommand(item, uri); + if (cmd) { + item.command = { + title: openComparison, + command: cmd.command, + arguments: cmd.arguments, + }; + } + + items.splice(0, 0, item); + } + } } return { @@ -235,7 +261,7 @@ export class GitTimelineProvider implements TimelineProvider { } private onConfigurationChanged(e: ConfigurationChangeEvent) { - if (e.affectsConfiguration('git.timeline.date') || e.affectsConfiguration('git.timeline.showAuthor')) { + if (e.affectsConfiguration('git.timeline.date') || e.affectsConfiguration('git.timeline.showAuthor') || e.affectsConfiguration('git.timeline.showUncommitted')) { this.fireChanged(); } } From 98d2ea44448ca34a32002690554d5a92dafa295d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 13 Apr 2022 17:21:40 +0200 Subject: [PATCH 042/245] smoke - restore workaround for #146785 --- test/automation/src/application.ts | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/test/automation/src/application.ts b/test/automation/src/application.ts index 9220fa6e047..fbb46a40d4d 100644 --- a/test/automation/src/application.ts +++ b/test/automation/src/application.ts @@ -121,7 +121,7 @@ export class Application { await code.waitForWindowIds(ids => ids.length > 0); // We need a rendered workbench - await measureAndLog(code.waitForElement('.monaco-workbench'), 'Application#checkWindowReady: wait for .monaco-workbench element', this.logger); + await this.checkWorkbenchReady(code); // Remote but not web: wait for a remote connection state change if (this.remote) { @@ -140,4 +140,28 @@ export class Application { }, 300 /* = 30s of retry */), 'Application#checkWindowReady: wait for remote indicator', this.logger); } } + + private async checkWorkbenchReady(code: Code): Promise { + const driver = code.driver; + + // Web / Legacy: just poll for workbench element + if (this.web) { + await measureAndLog(code.waitForElement('.monaco-workbench'), 'Application#checkWindowReady: wait for .monaco-workbench element', this.logger); + } + + // Desktop (playwright): we see hangs, where IPC messages + // are not delivered (https://github.com/microsoft/vscode/issues/146785) + // Workaround is to try to reload the window when that happens + else { + try { + await measureAndLog(code.waitForElement('.monaco-workbench', undefined, 100 /* 10s of retry */), 'Application#checkWindowReady: wait for .monaco-workbench element', this.logger); + } catch (error) { + this.logger.log(`checkWindowReady: giving up after 10s, reloading window and trying again...`); + + await driver.reload(); + + return this.checkWorkbenchReady(code); + } + } + } } From 675b8c3da07e8005b3480a458d0ad1becb7922ff Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 13 Apr 2022 17:37:52 +0200 Subject: [PATCH 043/245] smoke - fix compile --- test/automation/src/application.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/automation/src/application.ts b/test/automation/src/application.ts index fbb46a40d4d..23c9e630ea2 100644 --- a/test/automation/src/application.ts +++ b/test/automation/src/application.ts @@ -6,6 +6,7 @@ import { Workbench } from './workbench'; import { Code, launch, LaunchOptions } from './code'; import { Logger, measureAndLog } from './logger'; +import { PlaywrightDriver } from './playwrightDriver'; export const enum Quality { Dev, @@ -145,7 +146,7 @@ export class Application { const driver = code.driver; // Web / Legacy: just poll for workbench element - if (this.web) { + if (this.web || !(driver instanceof PlaywrightDriver)) { await measureAndLog(code.waitForElement('.monaco-workbench'), 'Application#checkWindowReady: wait for .monaco-workbench element', this.logger); } From 9a87fe7eaf2bcf29de4f5ec7c02044c410c95c0f Mon Sep 17 00:00:00 2001 From: aamunger Date: Mon, 11 Apr 2022 12:44:15 -0700 Subject: [PATCH 044/245] added setting for notebook output line height --- extensions/notebook-renderers/src/index.ts | 5 ++++- .../notebook/browser/notebook.contribution.ts | 6 ++++++ .../notebook/browser/notebookEditorWidget.ts | 2 +- .../browser/view/renderers/backLayerWebView.ts | 2 ++ .../contrib/notebook/common/notebookCommon.ts | 3 ++- .../contrib/notebook/common/notebookOptions.ts | 18 ++++++++++++++++-- 6 files changed, 31 insertions(+), 5 deletions(-) diff --git a/extensions/notebook-renderers/src/index.ts b/extensions/notebook-renderers/src/index.ts index 8ad65a8be63..1d6d75aa4f7 100644 --- a/extensions/notebook-renderers/src/index.ts +++ b/extensions/notebook-renderers/src/index.ts @@ -174,7 +174,7 @@ export const activate: ActivationFunction = (ctx) => { .output-plaintext, .output-stream, .traceback { - line-height: 22px; + line-height: var(--notebook-cell-output-line-height); font-family: var(--notebook-cell-output-font-family); white-space: pre-wrap; word-wrap: break-word; @@ -185,6 +185,9 @@ export const activate: ActivationFunction = (ctx) => { -ms-user-select: text; cursor: auto; } + span.output-stream { + display: inline-block; + } .output-plaintext .code-bold, .output-stream .code-bold, .traceback .code-bold { diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 2b793c40961..f1c5936099e 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -892,5 +892,11 @@ configurationRegistry.registerConfiguration({ enum: ['always', 'never', 'fromEditor'], default: 'fromEditor' }, + [NotebookSetting.outputLineHeight]: { + markdownDescription: nls.localize('notebook.outputLineHeight', "line height of the output text"), + type: 'number', + default: 22, + tags: ['notebookLayout'] + }, } }); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index aa1bcc7853f..3cc81adc47a 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -397,7 +397,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD this._updateForNotebookConfiguration(); } - if (e.compactView || e.focusIndicator || e.insertToolbarPosition || e.cellToolbarLocation || e.dragAndDropEnabled || e.fontSize || e.markupFontSize || e.insertToolbarAlignment) { + if (e.compactView || e.focusIndicator || e.insertToolbarPosition || e.cellToolbarLocation || e.dragAndDropEnabled || e.fontSize || e.markupFontSize || e.insertToolbarAlignment || e.outputLineHeight) { this._styleElement?.remove(); this._createLayoutStyles(); this._webview?.updateOptions({ diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 7bda661ed4e..4917457e7dc 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -81,6 +81,7 @@ export interface INotebookDelegateForWebview { interface BacklayerWebviewOptions { readonly outputNodePadding: number; readonly outputNodeLeftPadding: number; + readonly outputLineHeight: number; readonly previewNodePadding: number; readonly markdownLeftMargin: number; readonly leftMargin: number; @@ -205,6 +206,7 @@ export class BackLayerWebView extends Disposable { 'notebook-markdown-min-height': `${this.options.previewNodePadding * 2}px`, 'notebook-markup-font-size': typeof this.options.markupFontSize === 'number' && this.options.markupFontSize > 0 ? `${this.options.markupFontSize}px` : `calc(${this.options.fontSize}px * 1.2)`, 'notebook-cell-output-font-size': `${this.options.fontSize}px`, + 'notebook-cell-output-line-height': `${this.options.outputLineHeight}px`, 'notebook-cell-output-font-family': this.options.fontFamily, 'notebook-cell-markup-empty-content': nls.localize('notebook.emptyMarkdownPlaceholder', "Empty markdown cell, double click or press enter to edit."), 'notebook-cell-renderer-not-found-error': nls.localize({ diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index b93c8a7449f..4a2b3f9e405 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -913,7 +913,8 @@ export const NotebookSetting = { textOutputLineLimit: 'notebook.output.textLineLimit', globalToolbarShowLabel: 'notebook.globalToolbarShowLabel', markupFontSize: 'notebook.markup.fontSize', - interactiveWindowCollapseCodeCells: 'interactiveWindow.collapseCellInputCode' + interactiveWindowCollapseCodeCells: 'interactiveWindow.collapseCellInputCode', + outputLineHeight: 'notebook.outputLineHeight' } as const; export const enum CellStatusbarAlignment { diff --git a/src/vs/workbench/contrib/notebook/common/notebookOptions.ts b/src/vs/workbench/contrib/notebook/common/notebookOptions.ts index 9564a563def..91d23a63e48 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookOptions.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookOptions.ts @@ -62,6 +62,7 @@ export interface NotebookLayoutConfiguration { showFoldingControls: 'always' | 'mouseover'; dragAndDropEnabled: boolean; fontSize: number; + outputLineHeight: number; markupFontSize: number; focusIndicatorLeftMargin: number; editorOptionsCustomizations: any | undefined; @@ -87,6 +88,7 @@ export interface NotebookOptionsChangeEvent { readonly markupFontSize?: boolean; readonly editorOptionsCustomizations?: boolean; readonly interactiveWindowCollapseCodeCells?: boolean; + readonly outputLineHeight?: boolean; } const defaultConfigConstants = Object.freeze({ @@ -137,6 +139,7 @@ export class NotebookOptions extends Disposable { const markupFontSize = this.configurationService.getValue(NotebookSetting.markupFontSize); const editorOptionsCustomizations = this.configurationService.getValue(NotebookSetting.cellEditorOptionsCustomizations); const interactiveWindowCollapseCodeCells: InteractiveWindowCollapseCodeCells = this.configurationService.getValue(NotebookSetting.interactiveWindowCollapseCodeCells); + const outputLineHeight = this.configurationService.getValue(NotebookSetting.outputLineHeight); this._layoutConfiguration = { ...(compactView ? compactConfigConstants : defaultConfigConstants), @@ -166,6 +169,7 @@ export class NotebookOptions extends Disposable { insertToolbarAlignment, showFoldingControls, fontSize, + outputLineHeight, markupFontSize, editorOptionsCustomizations, focusIndicatorGap: 3, @@ -202,6 +206,7 @@ export class NotebookOptions extends Disposable { const markupFontSize = e.affectsConfiguration(NotebookSetting.markupFontSize); const editorOptionsCustomizations = e.affectsConfiguration(NotebookSetting.cellEditorOptionsCustomizations); const interactiveWindowCollapseCodeCells = e.affectsConfiguration(NotebookSetting.interactiveWindowCollapseCodeCells); + const outputLineHeight = e.affectsConfiguration(NotebookSetting.outputLineHeight); if ( !cellStatusBarVisibility @@ -219,7 +224,8 @@ export class NotebookOptions extends Disposable { && !fontSize && !markupFontSize && !editorOptionsCustomizations - && !interactiveWindowCollapseCodeCells) { + && !interactiveWindowCollapseCodeCells + && !outputLineHeight) { return; } @@ -293,8 +299,13 @@ export class NotebookOptions extends Disposable { configuration.interactiveWindowCollapseCodeCells = this.configurationService.getValue(NotebookSetting.interactiveWindowCollapseCodeCells); } + if (outputLineHeight) { + configuration.outputLineHeight = this.configurationService.getValue(NotebookSetting.outputLineHeight); + } + this._layoutConfiguration = Object.freeze(configuration); + // add new configuration to the event? // trigger event this._onDidChangeOptions.fire({ cellStatusBarVisibility, @@ -312,7 +323,8 @@ export class NotebookOptions extends Disposable { fontSize, markupFontSize, editorOptionsCustomizations, - interactiveWindowCollapseCodeCells + interactiveWindowCollapseCodeCells, + outputLineHeight }); } @@ -503,6 +515,7 @@ export class NotebookOptions extends Disposable { dragAndDropEnabled: this._layoutConfiguration.dragAndDropEnabled, fontSize: this._layoutConfiguration.fontSize, markupFontSize: this._layoutConfiguration.markupFontSize, + outputLineHeight: this._layoutConfiguration.outputLineHeight, }; } @@ -518,6 +531,7 @@ export class NotebookOptions extends Disposable { dragAndDropEnabled: false, fontSize: this._layoutConfiguration.fontSize, markupFontSize: this._layoutConfiguration.markupFontSize, + outputLineHeight: this._layoutConfiguration.outputLineHeight, }; } From 51ab78f535d8537e031e1ae77cac678ae22b0249 Mon Sep 17 00:00:00 2001 From: aamunger Date: Tue, 12 Apr 2022 08:32:14 -0700 Subject: [PATCH 045/245] added setting to override notebook output font family and size --- .../notebook/browser/notebook.contribution.ts | 13 +++++++- .../notebook/browser/notebookEditorWidget.ts | 2 +- .../view/renderers/backLayerWebView.ts | 8 +++-- .../contrib/notebook/common/notebookCommon.ts | 4 ++- .../notebook/common/notebookOptions.ts | 30 +++++++++++++++++++ 5 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index f1c5936099e..17c7255ad50 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -893,10 +893,21 @@ configurationRegistry.registerConfiguration({ default: 'fromEditor' }, [NotebookSetting.outputLineHeight]: { - markdownDescription: nls.localize('notebook.outputLineHeight', "line height of the output text"), + markdownDescription: nls.localize('notebook.outputLineHeight', "Line height of the output text"), type: 'number', default: 22, tags: ['notebookLayout'] }, + [NotebookSetting.outputFontSize]: { + markdownDescription: nls.localize('notebook.outputFontSize', "Font size for plain text outputs. When set to 0 `#editor.fontSize#` is used."), + type: 'number', + default: 0, + tags: ['notebookLayout'] + }, + [NotebookSetting.outputFontFamily]: { + markdownDescription: nls.localize('notebook.outputFontFamily', "The font family for plain text output of notebook cells. When set to empty, the `#editor.fontFamily#` is used."), + type: 'string', + tags: ['notebookLayout'] + }, } }); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 3cc81adc47a..15e56226ccd 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -397,7 +397,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD this._updateForNotebookConfiguration(); } - if (e.compactView || e.focusIndicator || e.insertToolbarPosition || e.cellToolbarLocation || e.dragAndDropEnabled || e.fontSize || e.markupFontSize || e.insertToolbarAlignment || e.outputLineHeight) { + if (e.compactView || e.focusIndicator || e.insertToolbarPosition || e.cellToolbarLocation || e.dragAndDropEnabled || e.fontSize || e.outputFontSize || e.fontFamily || e.outputFontFamily || e.markupFontSize || e.insertToolbarAlignment || e.outputLineHeight) { this._styleElement?.remove(); this._createLayoutStyles(); this._webview?.updateOptions({ diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 4917457e7dc..d5ad9679436 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -81,7 +81,6 @@ export interface INotebookDelegateForWebview { interface BacklayerWebviewOptions { readonly outputNodePadding: number; readonly outputNodeLeftPadding: number; - readonly outputLineHeight: number; readonly previewNodePadding: number; readonly markdownLeftMargin: number; readonly leftMargin: number; @@ -89,8 +88,11 @@ interface BacklayerWebviewOptions { readonly runGutter: number; readonly dragAndDropEnabled: boolean; readonly fontSize: number; + readonly outputFontSize: number; readonly fontFamily: string; + readonly outputFontFamily: string; readonly markupFontSize: number; + readonly outputLineHeight: number; } export class BackLayerWebView extends Disposable { @@ -205,9 +207,9 @@ export class BackLayerWebView extends Disposable { 'notebook-output-node-left-padding': `${this.options.outputNodeLeftPadding}px`, 'notebook-markdown-min-height': `${this.options.previewNodePadding * 2}px`, 'notebook-markup-font-size': typeof this.options.markupFontSize === 'number' && this.options.markupFontSize > 0 ? `${this.options.markupFontSize}px` : `calc(${this.options.fontSize}px * 1.2)`, - 'notebook-cell-output-font-size': `${this.options.fontSize}px`, + 'notebook-cell-output-font-size': `${this.options.outputFontSize || this.options.fontSize}px`, 'notebook-cell-output-line-height': `${this.options.outputLineHeight}px`, - 'notebook-cell-output-font-family': this.options.fontFamily, + 'notebook-cell-output-font-family': this.options.outputFontFamily || this.options.fontFamily, 'notebook-cell-markup-empty-content': nls.localize('notebook.emptyMarkdownPlaceholder', "Empty markdown cell, double click or press enter to edit."), 'notebook-cell-renderer-not-found-error': nls.localize({ key: 'notebook.error.rendererNotFound', diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 4a2b3f9e405..121ac0689da 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -914,7 +914,9 @@ export const NotebookSetting = { globalToolbarShowLabel: 'notebook.globalToolbarShowLabel', markupFontSize: 'notebook.markup.fontSize', interactiveWindowCollapseCodeCells: 'interactiveWindow.collapseCellInputCode', - outputLineHeight: 'notebook.outputLineHeight' + outputLineHeight: 'notebook.outputLineHeight', + outputFontSize: 'notebook.outputFontSize', + outputFontFamily: 'notebook.fontFamily' } as const; export const enum CellStatusbarAlignment { diff --git a/src/vs/workbench/contrib/notebook/common/notebookOptions.ts b/src/vs/workbench/contrib/notebook/common/notebookOptions.ts index 91d23a63e48..b7c9b89a926 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookOptions.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookOptions.ts @@ -62,6 +62,8 @@ export interface NotebookLayoutConfiguration { showFoldingControls: 'always' | 'mouseover'; dragAndDropEnabled: boolean; fontSize: number; + outputFontSize: number; + outputFontFamily: string; outputLineHeight: number; markupFontSize: number; focusIndicatorLeftMargin: number; @@ -85,6 +87,9 @@ export interface NotebookOptionsChangeEvent { readonly consolidatedRunButton?: boolean; readonly dragAndDropEnabled?: boolean; readonly fontSize?: boolean; + readonly outputFontSize?: boolean; + readonly fontFamily?: boolean; + readonly outputFontFamily?: boolean; readonly markupFontSize?: boolean; readonly editorOptionsCustomizations?: boolean; readonly interactiveWindowCollapseCodeCells?: boolean; @@ -136,6 +141,8 @@ export class NotebookOptions extends Disposable { const showFoldingControls = this._computeShowFoldingControlsOption(); // const { bottomToolbarGap, bottomToolbarHeight } = this._computeBottomToolbarDimensions(compactView, insertToolbarPosition, insertToolbarAlignment); const fontSize = this.configurationService.getValue('editor.fontSize'); + const outputFontSize = this.configurationService.getValue(NotebookSetting.outputFontSize); + const outputFontFamily = this.configurationService.getValue(NotebookSetting.outputFontFamily); const markupFontSize = this.configurationService.getValue(NotebookSetting.markupFontSize); const editorOptionsCustomizations = this.configurationService.getValue(NotebookSetting.cellEditorOptionsCustomizations); const interactiveWindowCollapseCodeCells: InteractiveWindowCollapseCodeCells = this.configurationService.getValue(NotebookSetting.interactiveWindowCollapseCodeCells); @@ -169,6 +176,8 @@ export class NotebookOptions extends Disposable { insertToolbarAlignment, showFoldingControls, fontSize, + outputFontSize, + outputFontFamily, outputLineHeight, markupFontSize, editorOptionsCustomizations, @@ -203,11 +212,15 @@ export class NotebookOptions extends Disposable { const showFoldingControls = e.affectsConfiguration(NotebookSetting.showFoldingControls); const dragAndDropEnabled = e.affectsConfiguration(NotebookSetting.dragAndDropEnabled); const fontSize = e.affectsConfiguration('editor.fontSize'); + const outputFontSize = e.affectsConfiguration(NotebookSetting.outputFontSize); + const fontFamily = e.affectsConfiguration('editor.fontFamily'); + const outputFontFamily = e.affectsConfiguration(NotebookSetting.outputFontFamily); const markupFontSize = e.affectsConfiguration(NotebookSetting.markupFontSize); const editorOptionsCustomizations = e.affectsConfiguration(NotebookSetting.cellEditorOptionsCustomizations); const interactiveWindowCollapseCodeCells = e.affectsConfiguration(NotebookSetting.interactiveWindowCollapseCodeCells); const outputLineHeight = e.affectsConfiguration(NotebookSetting.outputLineHeight); + if ( !cellStatusBarVisibility && !cellToolbarLocation @@ -222,6 +235,9 @@ export class NotebookOptions extends Disposable { && !showFoldingControls && !dragAndDropEnabled && !fontSize + && !outputFontSize + && !fontFamily + && !outputFontFamily && !markupFontSize && !editorOptionsCustomizations && !interactiveWindowCollapseCodeCells @@ -287,6 +303,14 @@ export class NotebookOptions extends Disposable { configuration.fontSize = this.configurationService.getValue('editor.fontSize'); } + if (outputFontSize) { + configuration.outputFontSize = this.configurationService.getValue(NotebookSetting.outputFontSize); + } + + if (outputFontFamily) { + configuration.outputFontFamily = this.configurationService.getValue(NotebookSetting.outputFontFamily); + } + if (markupFontSize) { configuration.markupFontSize = this.configurationService.getValue(NotebookSetting.markupFontSize); } @@ -321,6 +345,8 @@ export class NotebookOptions extends Disposable { consolidatedRunButton, dragAndDropEnabled, fontSize, + outputFontSize, + outputFontFamily, markupFontSize, editorOptionsCustomizations, interactiveWindowCollapseCodeCells, @@ -514,6 +540,8 @@ export class NotebookOptions extends Disposable { runGutter: this._layoutConfiguration.cellRunGutter, dragAndDropEnabled: this._layoutConfiguration.dragAndDropEnabled, fontSize: this._layoutConfiguration.fontSize, + outputFontSize: this._layoutConfiguration.outputFontSize, + outputFontFamily: this._layoutConfiguration.outputFontFamily, markupFontSize: this._layoutConfiguration.markupFontSize, outputLineHeight: this._layoutConfiguration.outputLineHeight, }; @@ -530,6 +558,8 @@ export class NotebookOptions extends Disposable { runGutter: 0, dragAndDropEnabled: false, fontSize: this._layoutConfiguration.fontSize, + outputFontSize: this._layoutConfiguration.outputFontSize, + outputFontFamily: this._layoutConfiguration.outputFontFamily, markupFontSize: this._layoutConfiguration.markupFontSize, outputLineHeight: this._layoutConfiguration.outputLineHeight, }; From fe38a7b3bd41d5f7fe0e29579308f625d68ea170 Mon Sep 17 00:00:00 2001 From: aamunger Date: Tue, 12 Apr 2022 12:46:09 -0700 Subject: [PATCH 046/245] line height as ratio when < 8 --- .../notebook/browser/notebook.contribution.ts | 6 +-- .../notebook/browser/notebookEditorWidget.ts | 2 +- .../contrib/notebook/common/notebookCommon.ts | 2 +- .../notebook/common/notebookOptions.ts | 42 ++++++++++++------- 4 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 17c7255ad50..b96059530d4 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -893,19 +893,19 @@ configurationRegistry.registerConfiguration({ default: 'fromEditor' }, [NotebookSetting.outputLineHeight]: { - markdownDescription: nls.localize('notebook.outputLineHeight', "Line height of the output text"), + markdownDescription: nls.localize('notebook.outputLineHeight', "Line height of the output text for notebook cells.\n - Values between 0 and 8 will be used as a multiplier with the font size.\n - Values greater than or equal to 8 will be used as effective values."), type: 'number', default: 22, tags: ['notebookLayout'] }, [NotebookSetting.outputFontSize]: { - markdownDescription: nls.localize('notebook.outputFontSize', "Font size for plain text outputs. When set to 0 `#editor.fontSize#` is used."), + markdownDescription: nls.localize('notebook.outputFontSize', "Font size for the output text for notebook cells. When set to 0 `#editor.fontSize#` is used."), type: 'number', default: 0, tags: ['notebookLayout'] }, [NotebookSetting.outputFontFamily]: { - markdownDescription: nls.localize('notebook.outputFontFamily', "The font family for plain text output of notebook cells. When set to empty, the `#editor.fontFamily#` is used."), + markdownDescription: nls.localize('notebook.outputFontFamily', "The font family for the output text for notebook cells. When set to empty, the `#editor.fontFamily#` is used."), type: 'string', tags: ['notebookLayout'] }, diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 15e56226ccd..93bcb157f61 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -397,7 +397,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD this._updateForNotebookConfiguration(); } - if (e.compactView || e.focusIndicator || e.insertToolbarPosition || e.cellToolbarLocation || e.dragAndDropEnabled || e.fontSize || e.outputFontSize || e.fontFamily || e.outputFontFamily || e.markupFontSize || e.insertToolbarAlignment || e.outputLineHeight) { + if (e.compactView || e.focusIndicator || e.insertToolbarPosition || e.cellToolbarLocation || e.dragAndDropEnabled || e.fontSize || e.outputFontSize || e.markupFontSize || e.insertToolbarAlignment || e.outputLineHeight) { this._styleElement?.remove(); this._createLayoutStyles(); this._webview?.updateOptions({ diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 121ac0689da..5f70d558da1 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -916,7 +916,7 @@ export const NotebookSetting = { interactiveWindowCollapseCodeCells: 'interactiveWindow.collapseCellInputCode', outputLineHeight: 'notebook.outputLineHeight', outputFontSize: 'notebook.outputFontSize', - outputFontFamily: 'notebook.fontFamily' + outputFontFamily: 'notebook.outputFontFamily' } as const; export const enum CellStatusbarAlignment { diff --git a/src/vs/workbench/contrib/notebook/common/notebookOptions.ts b/src/vs/workbench/contrib/notebook/common/notebookOptions.ts index b7c9b89a926..2964a52df79 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookOptions.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookOptions.ts @@ -88,8 +88,6 @@ export interface NotebookOptionsChangeEvent { readonly dragAndDropEnabled?: boolean; readonly fontSize?: boolean; readonly outputFontSize?: boolean; - readonly fontFamily?: boolean; - readonly outputFontFamily?: boolean; readonly markupFontSize?: boolean; readonly editorOptionsCustomizations?: boolean; readonly interactiveWindowCollapseCodeCells?: boolean; @@ -146,7 +144,7 @@ export class NotebookOptions extends Disposable { const markupFontSize = this.configurationService.getValue(NotebookSetting.markupFontSize); const editorOptionsCustomizations = this.configurationService.getValue(NotebookSetting.cellEditorOptionsCustomizations); const interactiveWindowCollapseCodeCells: InteractiveWindowCollapseCodeCells = this.configurationService.getValue(NotebookSetting.interactiveWindowCollapseCodeCells); - const outputLineHeight = this.configurationService.getValue(NotebookSetting.outputLineHeight); + const outputLineHeight = this._computeOutputLineHeight(); this._layoutConfiguration = { ...(compactView ? compactConfigConstants : defaultConfigConstants), @@ -198,6 +196,29 @@ export class NotebookOptions extends Disposable { })); } + private _computeOutputLineHeight(): number { + const minimumLineHeight = 8; + let lineHeight = this.configurationService.getValue(NotebookSetting.outputLineHeight); + + if (lineHeight < minimumLineHeight) { + // Values too small to be line heights in pixels are in ems. + let fontSize = this.configurationService.getValue(NotebookSetting.outputFontSize); + if (fontSize === 0) { + fontSize = this.configurationService.getValue('editor.fontSize'); + } + + lineHeight = lineHeight * fontSize; + } + + // Enforce integer, minimum constraints + lineHeight = Math.round(lineHeight); + if (lineHeight < minimumLineHeight) { + lineHeight = minimumLineHeight; + } + + return lineHeight; + } + private _updateConfiguration(e: IConfigurationChangeEvent) { const cellStatusBarVisibility = e.affectsConfiguration(NotebookSetting.showCellStatusBar); const cellToolbarLocation = e.affectsConfiguration(NotebookSetting.cellToolbarLocation); @@ -213,8 +234,6 @@ export class NotebookOptions extends Disposable { const dragAndDropEnabled = e.affectsConfiguration(NotebookSetting.dragAndDropEnabled); const fontSize = e.affectsConfiguration('editor.fontSize'); const outputFontSize = e.affectsConfiguration(NotebookSetting.outputFontSize); - const fontFamily = e.affectsConfiguration('editor.fontFamily'); - const outputFontFamily = e.affectsConfiguration(NotebookSetting.outputFontFamily); const markupFontSize = e.affectsConfiguration(NotebookSetting.markupFontSize); const editorOptionsCustomizations = e.affectsConfiguration(NotebookSetting.cellEditorOptionsCustomizations); const interactiveWindowCollapseCodeCells = e.affectsConfiguration(NotebookSetting.interactiveWindowCollapseCodeCells); @@ -236,8 +255,6 @@ export class NotebookOptions extends Disposable { && !dragAndDropEnabled && !fontSize && !outputFontSize - && !fontFamily - && !outputFontFamily && !markupFontSize && !editorOptionsCustomizations && !interactiveWindowCollapseCodeCells @@ -304,11 +321,7 @@ export class NotebookOptions extends Disposable { } if (outputFontSize) { - configuration.outputFontSize = this.configurationService.getValue(NotebookSetting.outputFontSize); - } - - if (outputFontFamily) { - configuration.outputFontFamily = this.configurationService.getValue(NotebookSetting.outputFontFamily); + configuration.outputFontSize = this.configurationService.getValue(NotebookSetting.outputFontSize) ?? configuration.fontSize; } if (markupFontSize) { @@ -323,8 +336,8 @@ export class NotebookOptions extends Disposable { configuration.interactiveWindowCollapseCodeCells = this.configurationService.getValue(NotebookSetting.interactiveWindowCollapseCodeCells); } - if (outputLineHeight) { - configuration.outputLineHeight = this.configurationService.getValue(NotebookSetting.outputLineHeight); + if (outputLineHeight || fontSize || outputFontSize) { + configuration.outputLineHeight = this._computeOutputLineHeight(); } this._layoutConfiguration = Object.freeze(configuration); @@ -346,7 +359,6 @@ export class NotebookOptions extends Disposable { dragAndDropEnabled, fontSize, outputFontSize, - outputFontFamily, markupFontSize, editorOptionsCustomizations, interactiveWindowCollapseCodeCells, From c4cdb552a553db1e235a7e58ae2ee2a7d3f9030b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 13 Apr 2022 19:19:58 +0200 Subject: [PATCH 047/245] Remove legacy smoke test driver (#147393) * wip * fix imports * more lipstick * fix? * fix --- build/gulpfile.vscode.js | 1 - src/buildfile.js | 1 - src/vs/code/electron-main/app.ts | 9 - src/vs/platform/driver/common/driver.ts | 38 --- src/vs/platform/driver/common/driverIpc.ts | 105 -------- .../platform/driver/electron-main/driver.ts | 244 ------------------ .../driver/electron-sandbox/driver.ts | 53 ---- src/vs/platform/driver/node/driver.ts | 138 ---------- src/vs/platform/environment/common/argv.ts | 4 - .../environment/common/environment.ts | 3 - .../environment/common/environmentService.ts | 2 - src/vs/platform/environment/node/argv.ts | 1 - .../platform/windows/electron-main/window.ts | 2 +- src/vs/workbench/electron-sandbox/window.ts | 27 +- test/automation/package.json | 8 +- test/automation/src/application.ts | 10 +- test/automation/src/code.ts | 95 ++----- test/automation/src/debug.ts | 2 +- test/automation/src/driver.js | 16 -- test/automation/src/electron.ts | 86 ------ test/automation/src/index.ts | 1 - test/automation/src/playwrightBrowser.ts | 6 +- test/automation/src/playwrightDriver.ts | 68 +++-- test/automation/src/playwrightElectron.ts | 6 +- test/automation/src/scm.ts | 2 +- .../tools/copy-driver-definition.js | 26 +- test/smoke/src/main.ts | 10 +- test/smoke/test/index.js | 4 +- 28 files changed, 82 insertions(+), 886 deletions(-) delete mode 100644 src/vs/platform/driver/common/driverIpc.ts delete mode 100644 src/vs/platform/driver/electron-main/driver.ts delete mode 100644 src/vs/platform/driver/node/driver.ts delete mode 100644 test/automation/src/driver.js diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 2f5a931e850..2a67103d2b9 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -50,7 +50,6 @@ const vscodeEntryPoints = _.flatten([ const vscodeResources = [ 'out-build/main.js', 'out-build/cli.js', - 'out-build/driver.js', 'out-build/bootstrap.js', 'out-build/bootstrap-fork.js', 'out-build/bootstrap-amd.js', diff --git a/src/buildfile.js b/src/buildfile.js index 4a174a51436..8c30339da6e 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -76,7 +76,6 @@ exports.code = [ createModuleDescription('vs/code/node/cliProcessMain', ['vs/code/node/cli']), createModuleDescription('vs/code/electron-sandbox/issue/issueReporterMain'), createModuleDescription('vs/code/electron-browser/sharedProcess/sharedProcessMain'), - createModuleDescription('vs/platform/driver/node/driver'), createModuleDescription('vs/code/electron-sandbox/processExplorer/processExplorerMain') ]; diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index f48212b5add..eec246062ff 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -37,7 +37,6 @@ import { ElectronExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/el import { IDiagnosticsService } from 'vs/platform/diagnostics/common/diagnostics'; import { DiagnosticsMainService, IDiagnosticsMainService } from 'vs/platform/diagnostics/electron-main/diagnosticsMainService'; import { DialogMainService, IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogMainService'; -import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver'; import { IEncryptionMainService } from 'vs/platform/encryption/common/encryptionService'; import { EncryptionMainService } from 'vs/platform/encryption/node/encryptionMainService'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; @@ -523,14 +522,6 @@ export class CodeApplication extends Disposable { // Services const appInstantiationService = await this.initServices(machineId, sharedProcess, sharedProcessReady); - // Create driver - if (this.environmentMainService.driverHandle) { - const server = await serveDriver(mainProcessElectronServer, this.environmentMainService.driverHandle, appInstantiationService); - - this.logService.info('Driver started at:', this.environmentMainService.driverHandle); - this._register(server); - } - // Setup Auth Handler this._register(appInstantiationService.createInstance(ProxyAuthHandler)); diff --git a/src/vs/platform/driver/common/driver.ts b/src/vs/platform/driver/common/driver.ts index 8a573487f6d..1c593be20d1 100644 --- a/src/vs/platform/driver/common/driver.ts +++ b/src/vs/platform/driver/common/driver.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; - // !! Do not remove the following START and END markers, they are parsed by the smoketest build //*START @@ -19,14 +17,7 @@ export interface IElement { } export interface ILocaleInfo { - /** - * The UI language used. - */ language: string; - - /** - * The requested locale - */ locale?: string; } @@ -36,27 +27,6 @@ export interface ILocalizedStrings { find: string; } -export interface IDriver { - readonly _serviceBrand: undefined; - - getWindowIds(): Promise; - startTracing(windowId: number, name: string): Promise; - stopTracing(windowId: number, name: string, persist: boolean): Promise; - exitApplication(): Promise; - dispatchKeybinding(windowId: number, keybinding: string): Promise; - click(windowId: number, selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise; - setValue(windowId: number, selector: string, text: string): Promise; - getTitle(windowId: number): Promise; - isActiveElement(windowId: number, selector: string): Promise; - getElements(windowId: number, selector: string, recursive?: boolean): Promise; - getElementXY(windowId: number, selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number; y: number }>; - typeInEditor(windowId: number, selector: string, text: string): Promise; - getTerminalBuffer(windowId: number, selector: string): Promise; - writeInTerminal(windowId: number, selector: string, text: string): Promise; - getLocaleInfo(windowId: number): Promise; - getLocalizedStrings(windowId: number): Promise; -} - export interface IWindowDriver { click(selector: string, xoffset?: number | undefined, yoffset?: number | undefined): Promise; setValue(selector: string, text: string): Promise; @@ -72,11 +42,3 @@ export interface IWindowDriver { exitApplication(): Promise; } //*END - -export const ID = 'driverService'; -export const IDriver = createDecorator(ID); - -export interface IWindowDriverRegistry { - registerWindowDriver(windowId: number): Promise; - reloadWindowDriver(windowId: number): Promise; -} diff --git a/src/vs/platform/driver/common/driverIpc.ts b/src/vs/platform/driver/common/driverIpc.ts deleted file mode 100644 index dc095ee06fc..00000000000 --- a/src/vs/platform/driver/common/driverIpc.ts +++ /dev/null @@ -1,105 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Event } from 'vs/base/common/event'; -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IElement, ILocaleInfo, ILocalizedStrings as ILocalizedStrings, IWindowDriver, IWindowDriverRegistry } from 'vs/platform/driver/common/driver'; - -export class WindowDriverChannel implements IServerChannel { - - constructor(private driver: IWindowDriver) { } - - listen(_: unknown, event: string): Event { - throw new Error(`No event found: ${event}`); - } - - call(_: unknown, command: string, arg?: any): Promise { - switch (command) { - case 'click': return this.driver.click(arg[0], arg[1], arg[2]); - case 'setValue': return this.driver.setValue(arg[0], arg[1]); - case 'getTitle': return this.driver.getTitle(); - case 'isActiveElement': return this.driver.isActiveElement(arg); - case 'getElements': return this.driver.getElements(arg[0], arg[1]); - case 'getElementXY': return this.driver.getElementXY(arg[0], arg[1], arg[2]); - case 'typeInEditor': return this.driver.typeInEditor(arg[0], arg[1]); - case 'getTerminalBuffer': return this.driver.getTerminalBuffer(arg); - case 'writeInTerminal': return this.driver.writeInTerminal(arg[0], arg[1]); - case 'getLocaleInfo': return this.driver.getLocaleInfo(); - case 'getLocalizedStrings': return this.driver.getLocalizedStrings(); - } - - throw new Error(`Call not found: ${command}`); - } -} - -export class WindowDriverChannelClient implements IWindowDriver { - - declare readonly _serviceBrand: undefined; - - constructor(private channel: IChannel) { } - - click(selector: string, xoffset?: number, yoffset?: number): Promise { - return this.channel.call('click', [selector, xoffset, yoffset]); - } - - setValue(selector: string, text: string): Promise { - return this.channel.call('setValue', [selector, text]); - } - - getTitle(): Promise { - return this.channel.call('getTitle'); - } - - isActiveElement(selector: string): Promise { - return this.channel.call('isActiveElement', selector); - } - - getElements(selector: string, recursive: boolean): Promise { - return this.channel.call('getElements', [selector, recursive]); - } - - getElementXY(selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number; y: number }> { - return this.channel.call('getElementXY', [selector, xoffset, yoffset]); - } - - typeInEditor(selector: string, text: string): Promise { - return this.channel.call('typeInEditor', [selector, text]); - } - - getTerminalBuffer(selector: string): Promise { - return this.channel.call('getTerminalBuffer', selector); - } - - writeInTerminal(selector: string, text: string): Promise { - return this.channel.call('writeInTerminal', [selector, text]); - } - - getLocaleInfo(): Promise { - return this.channel.call('getLocaleInfo'); - } - - getLocalizedStrings(): Promise { - return this.channel.call('getLocalizedStrings'); - } - - exitApplication(): Promise { - return this.channel.call('exitApplication'); - } -} - -export class WindowDriverRegistryChannelClient implements IWindowDriverRegistry { - - declare readonly _serviceBrand: undefined; - - constructor(private channel: IChannel) { } - - registerWindowDriver(windowId: number): Promise { - return this.channel.call('registerWindowDriver', windowId); - } - - reloadWindowDriver(windowId: number): Promise { - return this.channel.call('reloadWindowDriver', windowId); - } -} diff --git a/src/vs/platform/driver/electron-main/driver.ts b/src/vs/platform/driver/electron-main/driver.ts deleted file mode 100644 index 0e6956956b8..00000000000 --- a/src/vs/platform/driver/electron-main/driver.ts +++ /dev/null @@ -1,244 +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 { timeout } from 'vs/base/common/async'; -import { Emitter, Event } from 'vs/base/common/event'; -import { KeybindingParser } from 'vs/base/common/keybindingParser'; -import { KeyCode } from 'vs/base/common/keyCodes'; -import { SimpleKeybinding, ScanCodeBinding } from 'vs/base/common/keybindings'; -import { combinedDisposable, IDisposable } from 'vs/base/common/lifecycle'; -import { OS } from 'vs/base/common/platform'; -import { IPCServer, StaticRouter } from 'vs/base/parts/ipc/common/ipc'; -import { serve as serveNet } from 'vs/base/parts/ipc/node/ipc.net'; -import { IDriver, IElement, ILocaleInfo, ILocalizedStrings, IWindowDriver, IWindowDriverRegistry } from 'vs/platform/driver/common/driver'; -import { WindowDriverChannelClient } from 'vs/platform/driver/common/driverIpc'; -import { DriverChannel, WindowDriverRegistryChannel } from 'vs/platform/driver/node/driver'; -import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; -import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; -import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; -import { IFileService } from 'vs/platform/files/common/files'; -import { URI } from 'vs/base/common/uri'; -import { join } from 'vs/base/common/path'; -import { VSBuffer } from 'vs/base/common/buffer'; -import { ILogService } from 'vs/platform/log/common/log'; - -function isSilentKeyCode(keyCode: KeyCode) { - return keyCode < KeyCode.Digit0; -} - -export class Driver implements IDriver, IWindowDriverRegistry { - - declare readonly _serviceBrand: undefined; - - private registeredWindowIds = new Set(); - private reloadingWindowIds = new Set(); - private readonly onDidReloadingChange = new Emitter(); - - constructor( - private windowServer: IPCServer, - @IWindowsMainService private readonly windowsMainService: IWindowsMainService, - @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, - @IFileService private readonly fileService: IFileService, - @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, - @ILogService private readonly logService: ILogService - ) { } - - async registerWindowDriver(windowId: number): Promise { - this.logService.info(`[driver] registerWindowDriver(${windowId})`); - - this.registeredWindowIds.add(windowId); - this.reloadingWindowIds.delete(windowId); - this.onDidReloadingChange.fire(); - } - - async reloadWindowDriver(windowId: number): Promise { - this.logService.info(`[driver] reloadWindowDriver(${windowId})`); - - this.reloadingWindowIds.add(windowId); - } - - async getWindowIds(): Promise { - const windowIds = this.windowsMainService.getWindows() - .map(window => window.id) - .filter(windowId => this.registeredWindowIds.has(windowId) && !this.reloadingWindowIds.has(windowId)); - - return windowIds; - } - - - async startTracing(windowId: number, name: string): Promise { - // ignore - tracing is not implemented yet - } - - async stopTracing(windowId: number, name: string, persist: boolean): Promise { - if (!persist) { - return; - } - - const raw = await this.capturePage(windowId); - const buffer = Buffer.from(raw, 'base64'); - - await this.fileService.writeFile(URI.file(join(this.environmentMainService.logsPath, `${name}.png`)), VSBuffer.wrap(buffer)); - } - - private async capturePage(windowId: number): Promise { - const window = this.windowsMainService.getWindowById(windowId) ?? this.windowsMainService.getLastActiveWindow(); // fallback to active window to ensure we capture window - if (!window?.win) { - throw new Error('Invalid window'); - } - - const webContents = window.win.webContents; - const image = await webContents.capturePage(); - return image.toPNG().toString('base64'); - } - - async exitApplication(): Promise { - this.logService.info(`[driver] exitApplication()`); - - await this.lifecycleMainService.quit(); - } - - async dispatchKeybinding(windowId: number, keybinding: string): Promise { - await this.whenUnfrozen(windowId); - - const parts = KeybindingParser.parseUserBinding(keybinding); - - for (let part of parts) { - await this._dispatchKeybinding(windowId, part); - } - } - - private async _dispatchKeybinding(windowId: number, keybinding: SimpleKeybinding | ScanCodeBinding): Promise { - if (keybinding instanceof ScanCodeBinding) { - throw new Error('ScanCodeBindings not supported'); - } - - const window = this.windowsMainService.getWindowById(windowId); - if (!window?.win) { - throw new Error('Invalid window'); - } - const webContents = window.win.webContents; - const noModifiedKeybinding = new SimpleKeybinding(false, false, false, false, keybinding.keyCode); - const resolvedKeybinding = new USLayoutResolvedKeybinding(noModifiedKeybinding.toChord(), OS); - const keyCode = resolvedKeybinding.getElectronAccelerator(); - - const modifiers: string[] = []; - - if (keybinding.ctrlKey) { - modifiers.push('ctrl'); - } - - if (keybinding.metaKey) { - modifiers.push('meta'); - } - - if (keybinding.shiftKey) { - modifiers.push('shift'); - } - - if (keybinding.altKey) { - modifiers.push('alt'); - } - - webContents.sendInputEvent({ type: 'keyDown', keyCode, modifiers } as any); - - if (!isSilentKeyCode(keybinding.keyCode)) { - webContents.sendInputEvent({ type: 'char', keyCode, modifiers } as any); - } - - webContents.sendInputEvent({ type: 'keyUp', keyCode, modifiers } as any); - - await timeout(100); - } - - async click(windowId: number, selector: string, xoffset?: number, yoffset?: number): Promise { - const windowDriver = await this.getWindowDriver(windowId); - await windowDriver.click(selector, xoffset, yoffset); - } - - async setValue(windowId: number, selector: string, text: string): Promise { - const windowDriver = await this.getWindowDriver(windowId); - await windowDriver.setValue(selector, text); - } - - async getTitle(windowId: number): Promise { - const windowDriver = await this.getWindowDriver(windowId); - return await windowDriver.getTitle(); - } - - async isActiveElement(windowId: number, selector: string): Promise { - const windowDriver = await this.getWindowDriver(windowId); - return await windowDriver.isActiveElement(selector); - } - - async getElements(windowId: number, selector: string, recursive: boolean): Promise { - const windowDriver = await this.getWindowDriver(windowId); - return await windowDriver.getElements(selector, recursive); - } - - async getElementXY(windowId: number, selector: string, xoffset?: number, yoffset?: number): Promise<{ x: number; y: number }> { - const windowDriver = await this.getWindowDriver(windowId); - return await windowDriver.getElementXY(selector, xoffset, yoffset); - } - - async typeInEditor(windowId: number, selector: string, text: string): Promise { - const windowDriver = await this.getWindowDriver(windowId); - await windowDriver.typeInEditor(selector, text); - } - - async getTerminalBuffer(windowId: number, selector: string): Promise { - const windowDriver = await this.getWindowDriver(windowId); - return await windowDriver.getTerminalBuffer(selector); - } - - async writeInTerminal(windowId: number, selector: string, text: string): Promise { - const windowDriver = await this.getWindowDriver(windowId); - await windowDriver.writeInTerminal(selector, text); - } - - async getLocaleInfo(windowId: number): Promise { - const windowDriver = await this.getWindowDriver(windowId); - return await windowDriver.getLocaleInfo(); - } - - async getLocalizedStrings(windowId: number): Promise { - const windowDriver = await this.getWindowDriver(windowId); - return await windowDriver.getLocalizedStrings(); - } - - private async getWindowDriver(windowId: number): Promise { - await this.whenUnfrozen(windowId); - - const id = `window:${windowId}`; - const router = new StaticRouter(ctx => ctx === id); - const windowDriverChannel = this.windowServer.getChannel('windowDriver', router); - return new WindowDriverChannelClient(windowDriverChannel); - } - - private async whenUnfrozen(windowId: number): Promise { - while (this.reloadingWindowIds.has(windowId)) { - await Event.toPromise(this.onDidReloadingChange.event); - } - } -} - -export async function serve( - windowServer: IPCServer, - handle: string, - instantiationService: IInstantiationService -): Promise { - const driver = instantiationService.createInstance(Driver, windowServer); - - const windowDriverRegistryChannel = new WindowDriverRegistryChannel(driver); - windowServer.registerChannel('windowDriverRegistry', windowDriverRegistryChannel); - - const server = await serveNet(handle); - const channel = new DriverChannel(driver); - server.registerChannel('driver', channel); - - return combinedDisposable(server, windowServer); -} diff --git a/src/vs/platform/driver/electron-sandbox/driver.ts b/src/vs/platform/driver/electron-sandbox/driver.ts index b398749230b..fb9b9a596ff 100644 --- a/src/vs/platform/driver/electron-sandbox/driver.ts +++ b/src/vs/platform/driver/electron-sandbox/driver.ts @@ -3,13 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { timeout } from 'vs/base/common/async'; -import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { BrowserWindowDriver } from 'vs/platform/driver/browser/driver'; -import { WindowDriverChannel, WindowDriverRegistryChannelClient } from 'vs/platform/driver/common/driverIpc'; -import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services'; -import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; interface INativeWindowDriverHelper { exitApplication(): Promise; @@ -29,50 +23,3 @@ class NativeWindowDriver extends BrowserWindowDriver { export function registerWindowDriver(helper: INativeWindowDriverHelper): void { Object.assign(window, { driver: new NativeWindowDriver(helper) }); } - -class LegacyNativeWindowDriver extends BrowserWindowDriver { - - constructor( - @INativeHostService private readonly nativeHostService: INativeHostService - ) { - super(); - } - - override click(selector: string, xoffset?: number, yoffset?: number): Promise { - const offset = typeof xoffset === 'number' && typeof yoffset === 'number' ? { x: xoffset, y: yoffset } : undefined; - - return this.doClick(selector, 1, offset); - } - - private async doClick(selector: string, clickCount: number, offset?: { x: number; y: number }): Promise { - const { x, y } = await this._getElementXY(selector, offset); - - await this.nativeHostService.sendInputEvent({ type: 'mouseDown', x, y, button: 'left', clickCount } as any); - await timeout(10); - - await this.nativeHostService.sendInputEvent({ type: 'mouseUp', x, y, button: 'left', clickCount } as any); - await timeout(100); - } -} - -/** - * Old school window driver that is implemented by us - * from the main process. - * - * @deprecated - */ -export async function registerLegacyWindowDriver(accessor: ServicesAccessor, windowId: number): Promise { - const instantiationService = accessor.get(IInstantiationService); - const mainProcessService = accessor.get(IMainProcessService); - - const windowDriver = instantiationService.createInstance(LegacyNativeWindowDriver); - const windowDriverChannel = new WindowDriverChannel(windowDriver); - mainProcessService.registerChannel('windowDriver', windowDriverChannel); - - const windowDriverRegistryChannel = mainProcessService.getChannel('windowDriverRegistry'); - const windowDriverRegistry = new WindowDriverRegistryChannelClient(windowDriverRegistryChannel); - - await windowDriverRegistry.registerWindowDriver(windowId); - - return toDisposable(() => windowDriverRegistry.reloadWindowDriver(windowId)); -} diff --git a/src/vs/platform/driver/node/driver.ts b/src/vs/platform/driver/node/driver.ts deleted file mode 100644 index 6ce00b50e53..00000000000 --- a/src/vs/platform/driver/node/driver.ts +++ /dev/null @@ -1,138 +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 { Event } from 'vs/base/common/event'; -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { Client } from 'vs/base/parts/ipc/common/ipc.net'; -import { connect as connectNet } from 'vs/base/parts/ipc/node/ipc.net'; -import { IDriver, IElement, ILocaleInfo, ILocalizedStrings, IWindowDriverRegistry } from 'vs/platform/driver/common/driver'; - -export class DriverChannel implements IServerChannel { - - constructor(private driver: IDriver) { } - - listen(_: unknown, event: string): Event { - throw new Error('No event found'); - } - - call(_: unknown, command: string, arg?: any): Promise { - switch (command) { - case 'getWindowIds': return this.driver.getWindowIds(); - case 'startTracing': return this.driver.startTracing(arg[0], arg[1]); - case 'stopTracing': return this.driver.stopTracing(arg[0], arg[1], arg[2]); - case 'exitApplication': return this.driver.exitApplication(); - case 'dispatchKeybinding': return this.driver.dispatchKeybinding(arg[0], arg[1]); - case 'click': return this.driver.click(arg[0], arg[1], arg[2], arg[3]); - case 'setValue': return this.driver.setValue(arg[0], arg[1], arg[2]); - case 'getTitle': return this.driver.getTitle(arg[0]); - case 'isActiveElement': return this.driver.isActiveElement(arg[0], arg[1]); - case 'getElements': return this.driver.getElements(arg[0], arg[1], arg[2]); - case 'getElementXY': return this.driver.getElementXY(arg[0], arg[1], arg[2]); - case 'typeInEditor': return this.driver.typeInEditor(arg[0], arg[1], arg[2]); - case 'getTerminalBuffer': return this.driver.getTerminalBuffer(arg[0], arg[1]); - case 'writeInTerminal': return this.driver.writeInTerminal(arg[0], arg[1], arg[2]); - case 'getLocaleInfo': return this.driver.getLocaleInfo(arg); - case 'getLocalizedStrings': return this.driver.getLocalizedStrings(arg); - } - - throw new Error(`Call not found: ${command}`); - } -} - -export class DriverChannelClient implements IDriver { - - declare readonly _serviceBrand: undefined; - - constructor(private channel: IChannel) { } - - getWindowIds(): Promise { - return this.channel.call('getWindowIds'); - } - - startTracing(windowId: number, name: string): Promise { - return this.channel.call('startTracing', [windowId, name]); - } - - stopTracing(windowId: number, name: string, persist: boolean): Promise { - return this.channel.call('stopTracing', [windowId, name, persist]); - } - - exitApplication(): Promise { - return this.channel.call('exitApplication'); - } - - dispatchKeybinding(windowId: number, keybinding: string): Promise { - return this.channel.call('dispatchKeybinding', [windowId, keybinding]); - } - - click(windowId: number, selector: string, xoffset: number | undefined, yoffset: number | undefined): Promise { - return this.channel.call('click', [windowId, selector, xoffset, yoffset]); - } - - setValue(windowId: number, selector: string, text: string): Promise { - return this.channel.call('setValue', [windowId, selector, text]); - } - - getTitle(windowId: number): Promise { - return this.channel.call('getTitle', [windowId]); - } - - isActiveElement(windowId: number, selector: string): Promise { - return this.channel.call('isActiveElement', [windowId, selector]); - } - - getElements(windowId: number, selector: string, recursive: boolean): Promise { - return this.channel.call('getElements', [windowId, selector, recursive]); - } - - getElementXY(windowId: number, selector: string, xoffset: number | undefined, yoffset: number | undefined): Promise<{ x: number; y: number }> { - return this.channel.call('getElementXY', [windowId, selector, xoffset, yoffset]); - } - - typeInEditor(windowId: number, selector: string, text: string): Promise { - return this.channel.call('typeInEditor', [windowId, selector, text]); - } - - getTerminalBuffer(windowId: number, selector: string): Promise { - return this.channel.call('getTerminalBuffer', [windowId, selector]); - } - - writeInTerminal(windowId: number, selector: string, text: string): Promise { - return this.channel.call('writeInTerminal', [windowId, selector, text]); - } - - getLocaleInfo(windowId: number): Promise { - return this.channel.call('getLocaleInfo', windowId); - } - - getLocalizedStrings(windowId: number): Promise { - return this.channel.call('getLocalizedStrings', windowId); - } -} - -export class WindowDriverRegistryChannel implements IServerChannel { - - constructor(private registry: IWindowDriverRegistry) { } - - listen(_: unknown, event: string): Event { - throw new Error(`Event not found: ${event}`); - } - - call(_: unknown, command: string, arg?: any): Promise { - switch (command) { - case 'registerWindowDriver': return this.registry.registerWindowDriver(arg); - case 'reloadWindowDriver': return this.registry.reloadWindowDriver(arg); - } - - throw new Error(`Call not found: ${command}`); - } -} - -export async function connect(handle: string): Promise<{ client: Client; driver: IDriver }> { - const client = await connectNet(handle, 'driverClient'); - const channel = client.getChannel('driver'); - const driver = new DriverChannelClient(channel); - return { client, driver }; -} diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index ccac10545b5..b2b4500a614 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -79,10 +79,6 @@ export interface NativeParsedArgs { 'max-memory'?: string; 'file-write'?: boolean; 'file-chmod'?: boolean; - /** - * @deprecated use `enable-smoke-test-driver` - */ - 'driver'?: string; 'enable-smoke-test-driver'?: boolean; 'remote'?: string; 'force'?: boolean; diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 28a690c69bb..56531a3a784 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -132,9 +132,6 @@ export interface INativeEnvironmentService extends IEnvironmentService { extensionsDownloadPath: string; builtinExtensionsPath: string; - // --- smoke test support - driverHandle?: string; - // --- use keytar for credentials disableKeytar?: boolean; diff --git a/src/vs/platform/environment/common/environmentService.ts b/src/vs/platform/environment/common/environmentService.ts index ac02eadcbdb..fcea3f58023 100644 --- a/src/vs/platform/environment/common/environmentService.ts +++ b/src/vs/platform/environment/common/environmentService.ts @@ -230,8 +230,6 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron get crashReporterId(): string | undefined { return this.args['crash-reporter-id']; } get crashReporterDirectory(): string | undefined { return this.args['crash-reporter-directory']; } - get driverHandle(): string | undefined { return this.args['driver']; } - @memoize get telemetryLogResource(): URI { return URI.file(join(this.logsPath, 'telemetry.log')); } get disableTelemetry(): boolean { return !!this.args['disable-telemetry']; } diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 1d7aadc1f14..062ce48a580 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -98,7 +98,6 @@ export const OPTIONS: OptionDescriptions> = { 'inspect-brk-search': { type: 'string', deprecates: ['debugBrkSearch'] }, 'export-default-configuration': { type: 'string' }, 'install-source': { type: 'string' }, - 'driver': { type: 'string' }, 'enable-smoke-test-driver': { type: 'boolean' }, 'logExtensionHostCommunication': { type: 'boolean' }, 'skip-release-notes': { type: 'boolean' }, diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index 77171dabff1..aca98a776db 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -545,7 +545,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { } // If we run smoke tests, we never want to show a blocking dialog - if (this.environmentMainService.driverHandle) { + if (this.environmentMainService.args['enable-smoke-test-driver']) { this.destroyWindow(false); return; } diff --git a/src/vs/workbench/electron-sandbox/window.ts b/src/vs/workbench/electron-sandbox/window.ts index 8617e328264..dc10761c214 100644 --- a/src/vs/workbench/electron-sandbox/window.ts +++ b/src/vs/workbench/electron-sandbox/window.ts @@ -63,7 +63,7 @@ import { whenEditorClosed } from 'vs/workbench/browser/editor'; import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; import { toErrorMessage } from 'vs/base/common/errorMessage'; -import { registerLegacyWindowDriver, registerWindowDriver } from 'vs/platform/driver/electron-sandbox/driver'; +import { registerWindowDriver } from 'vs/platform/driver/electron-sandbox/driver'; export class NativeWindow extends Disposable { @@ -640,25 +640,18 @@ export class NativeWindow extends Disposable { } // Smoke Test Driver - this.setupDriver(); + if (this.environmentService.enableSmokeTestDriver) { + this.setupDriver(); + } } private setupDriver(): void { - - // Modern Driver - if (this.environmentService.enableSmokeTestDriver) { - const that = this; - registerWindowDriver({ - async exitApplication(): Promise { - return that.nativeHostService.quit(); - } - }); - } - - // Legacy Driver (TODO@bpasero remove me eventually) - else if (this.environmentService.args.driver) { - this.instantiationService.invokeFunction(async accessor => this._register(await registerLegacyWindowDriver(accessor, this.nativeHostService.windowId))); - } + const that = this; + registerWindowDriver({ + async exitApplication(): Promise { + return that.nativeHostService.quit(); + } + }); } private setupOpenHandlers(): void { diff --git a/test/automation/package.json b/test/automation/package.json index 7a7b3c5a560..96397298161 100644 --- a/test/automation/package.json +++ b/test/automation/package.json @@ -9,13 +9,11 @@ "main": "./out/index.js", "private": true, "scripts": { - "compile": "npm run copy-driver && npm run copy-driver-definition && node ../../node_modules/typescript/bin/tsc", - "watch": "npm-run-all -lp watch-driver watch-driver-definition watch-tsc", + "compile": "npm run copy-driver-definition && node ../../node_modules/typescript/bin/tsc", + "watch": "npm-run-all -lp watch-driver-definition watch-tsc", "watch-tsc": "node ../../node_modules/typescript/bin/tsc --watch --preserveWatchOutput", - "copy-driver": "cpx src/driver.js out/", - "watch-driver": "cpx src/driver.js out/ -w", "copy-driver-definition": "node tools/copy-driver-definition.js", - "watch-driver-definition": "watch \"node tools/copy-driver-definition.js\" ../../src/vs/platform/driver/node", + "watch-driver-definition": "watch \"node tools/copy-driver-definition.js\"", "copy-package-version": "node tools/copy-package-version.js", "prepublishOnly": "npm run copy-package-version" }, diff --git a/test/automation/src/application.ts b/test/automation/src/application.ts index 23c9e630ea2..8914ed55982 100644 --- a/test/automation/src/application.ts +++ b/test/automation/src/application.ts @@ -16,8 +16,7 @@ export const enum Quality { export interface ApplicationOptions extends LaunchOptions { quality: Quality; - workspacePath: string; - waitTime: number; + readonly workspacePath: string; } export class Application { @@ -49,10 +48,6 @@ export class Application { return !!this.options.web; } - get legacy(): boolean { - return !!this.options.legacy; - } - private _workspacePathOrFolder: string; get workspacePathOrFolder(): string { return this._workspacePathOrFolder; @@ -118,9 +113,6 @@ export class Application { private async checkWindowReady(code: Code): Promise { - // This is legacy and will be removed when our old driver removes - await code.waitForWindowIds(ids => ids.length > 0); - // We need a rendered workbench await this.checkWorkbenchReady(code); diff --git a/test/automation/src/code.ts b/test/automation/src/code.ts index f50d69e83fa..5e6b52c7d73 100644 --- a/test/automation/src/code.ts +++ b/test/automation/src/code.ts @@ -6,14 +6,14 @@ import { join } from 'path'; import * as os from 'os'; import * as cp from 'child_process'; -import { IDriver, IDisposable, IElement, Thenable, ILocalizedStrings, ILocaleInfo } from './driver'; -import { launch as launchElectron } from './electron'; +import { IElement, ILocalizedStrings, ILocaleInfo } from './driver'; import { launch as launchPlaywrightBrowser } from './playwrightBrowser'; import { launch as launchPlaywrightElectron } from './playwrightElectron'; import { Logger, measureAndLog } from './logger'; import { copyExtension } from './extensions'; import * as treekill from 'tree-kill'; import { teardown } from './processes'; +import { PlaywrightDriver } from './playwrightDriver'; const rootPath = join(__dirname, '../../..'); @@ -28,7 +28,6 @@ export interface LaunchOptions { readonly extraArgs?: string[]; readonly remote?: boolean; readonly web?: boolean; - readonly legacy?: boolean; readonly tracing?: boolean; readonly headless?: boolean; readonly browser?: 'chromium' | 'webkit' | 'firefox'; @@ -80,37 +79,27 @@ export async function launch(options: LaunchOptions): Promise { // Browser smoke tests if (options.web) { - const { serverProcess, client, driver } = await measureAndLog(launchPlaywrightBrowser(options), 'launch playwright (browser)', options.logger); + const { serverProcess, driver } = await measureAndLog(launchPlaywrightBrowser(options), 'launch playwright (browser)', options.logger); registerInstance(serverProcess, options.logger, 'server'); - return new Code(client, driver, options.logger, serverProcess); + return new Code(driver, options.logger, serverProcess); } // Electron smoke tests (playwright) - else if (!options.legacy) { - const { electronProcess, client, driver } = await measureAndLog(launchPlaywrightElectron(options), 'launch playwright (electron)', options.logger); - registerInstance(electronProcess, options.logger, 'electron'); - - return new Code(client, driver, options.logger, electronProcess); - } - - // Electron smoke tests (legacy driver) else { - const { electronProcess, client, driver } = await measureAndLog(launchElectron(options), 'launch electron', options.logger); + const { electronProcess, driver } = await measureAndLog(launchPlaywrightElectron(options), 'launch playwright (electron)', options.logger); registerInstance(electronProcess, options.logger, 'electron'); - return new Code(client, driver, options.logger, electronProcess); + return new Code(driver, options.logger, electronProcess); } } export class Code { - private _activeWindowId: number | undefined = undefined; - readonly driver: IDriver; + readonly driver: PlaywrightDriver; constructor( - private client: IDisposable, - driver: IDriver, + driver: PlaywrightDriver, readonly logger: Logger, private readonly mainProcess: cp.ChildProcess ) { @@ -134,22 +123,15 @@ export class Code { } async startTracing(name: string): Promise { - const windowId = await this.getActiveWindowId(); - return await this.driver.startTracing(windowId, name); + return await this.driver.startTracing(name); } async stopTracing(name: string, persist: boolean): Promise { - const windowId = await this.getActiveWindowId(); - return await this.driver.stopTracing(windowId, name, persist); - } - - async waitForWindowIds(accept: (windowIds: number[]) => boolean): Promise { - await this.poll(() => this.driver.getWindowIds(), accept, `get window ids`); + return await this.driver.stopTracing(name, persist); } async dispatchKeybinding(keybinding: string): Promise { - const windowId = await this.getActiveWindowId(); - await this.driver.dispatchKeybinding(windowId, keybinding); + await this.driver.dispatchKeybinding(keybinding); } async exit(): Promise { @@ -195,17 +177,14 @@ export class Code { } } })(); - }).finally(() => { - this.dispose(); }), 'Code#exit()', this.logger); } async waitForTextContent(selector: string, textContent?: string, accept?: (result: string) => boolean, retryCount?: number): Promise { - const windowId = await this.getActiveWindowId(); accept = accept || (result => textContent !== undefined ? textContent === result : !!result); return await this.poll( - () => this.driver.getElements(windowId, selector).then(els => els.length > 0 ? Promise.resolve(els[0].textContent) : Promise.reject(new Error('Element not found for textContent'))), + () => this.driver.getElements(selector).then(els => els.length > 0 ? Promise.resolve(els[0].textContent) : Promise.reject(new Error('Element not found for textContent'))), s => accept!(typeof s === 'string' ? s : ''), `get text content '${selector}'`, retryCount @@ -213,77 +192,51 @@ export class Code { } async waitAndClick(selector: string, xoffset?: number, yoffset?: number, retryCount: number = 200): Promise { - const windowId = await this.getActiveWindowId(); - await this.poll(() => this.driver.click(windowId, selector, xoffset, yoffset), () => true, `click '${selector}'`, retryCount); + await this.poll(() => this.driver.click(selector, xoffset, yoffset), () => true, `click '${selector}'`, retryCount); } async waitForSetValue(selector: string, value: string): Promise { - const windowId = await this.getActiveWindowId(); - await this.poll(() => this.driver.setValue(windowId, selector, value), () => true, `set value '${selector}'`); + await this.poll(() => this.driver.setValue(selector, value), () => true, `set value '${selector}'`); } async waitForElements(selector: string, recursive: boolean, accept: (result: IElement[]) => boolean = result => result.length > 0): Promise { - const windowId = await this.getActiveWindowId(); - return await this.poll(() => this.driver.getElements(windowId, selector, recursive), accept, `get elements '${selector}'`); + return await this.poll(() => this.driver.getElements(selector, recursive), accept, `get elements '${selector}'`); } async waitForElement(selector: string, accept: (result: IElement | undefined) => boolean = result => !!result, retryCount: number = 200): Promise { - const windowId = await this.getActiveWindowId(); - return await this.poll(() => this.driver.getElements(windowId, selector).then(els => els[0]), accept, `get element '${selector}'`, retryCount); + return await this.poll(() => this.driver.getElements(selector).then(els => els[0]), accept, `get element '${selector}'`, retryCount); } async waitForActiveElement(selector: string, retryCount: number = 200): Promise { - const windowId = await this.getActiveWindowId(); - await this.poll(() => this.driver.isActiveElement(windowId, selector), r => r, `is active element '${selector}'`, retryCount); + await this.poll(() => this.driver.isActiveElement(selector), r => r, `is active element '${selector}'`, retryCount); } async waitForTitle(accept: (title: string) => boolean): Promise { - const windowId = await this.getActiveWindowId(); - await this.poll(() => this.driver.getTitle(windowId), accept, `get title`); + await this.poll(() => this.driver.getTitle(), accept, `get title`); } async waitForTypeInEditor(selector: string, text: string): Promise { - const windowId = await this.getActiveWindowId(); - await this.poll(() => this.driver.typeInEditor(windowId, selector, text), () => true, `type in editor '${selector}'`); + await this.poll(() => this.driver.typeInEditor(selector, text), () => true, `type in editor '${selector}'`); } async waitForTerminalBuffer(selector: string, accept: (result: string[]) => boolean): Promise { - const windowId = await this.getActiveWindowId(); - await this.poll(() => this.driver.getTerminalBuffer(windowId, selector), accept, `get terminal buffer '${selector}'`); + await this.poll(() => this.driver.getTerminalBuffer(selector), accept, `get terminal buffer '${selector}'`); } async writeInTerminal(selector: string, value: string): Promise { - const windowId = await this.getActiveWindowId(); - await this.poll(() => this.driver.writeInTerminal(windowId, selector, value), () => true, `writeInTerminal '${selector}'`); + await this.poll(() => this.driver.writeInTerminal(selector, value), () => true, `writeInTerminal '${selector}'`); } async getLocaleInfo(): Promise { - const windowId = await this.getActiveWindowId(); - return this.driver.getLocaleInfo(windowId); + return this.driver.getLocaleInfo(); } async getLocalizedStrings(): Promise { - const windowId = await this.getActiveWindowId(); - return this.driver.getLocalizedStrings(windowId); - } - - private async getActiveWindowId(): Promise { - if (typeof this._activeWindowId !== 'number') { - this.logger.log('getActiveWindowId(): begin'); - const windows = await this.driver.getWindowIds(); - this._activeWindowId = windows[0]; - this.logger.log(`getActiveWindowId(): end (windowId=${this._activeWindowId})`); - } - - return this._activeWindowId; - } - - dispose(): void { - this.client.dispose(); + return this.driver.getLocalizedStrings(); } private async poll( - fn: () => Thenable, + fn: () => Promise, acceptFn: (result: T) => boolean, timeoutMessage: string, retryCount = 200, diff --git a/test/automation/src/debug.ts b/test/automation/src/debug.ts index eedc400451f..b7b7d427f4b 100644 --- a/test/automation/src/debug.ts +++ b/test/automation/src/debug.ts @@ -8,7 +8,7 @@ import { Commands } from './workbench'; import { Code, findElement } from './code'; import { Editors } from './editors'; import { Editor } from './editor'; -import { IElement } from '../src/driver'; +import { IElement } from './driver'; const VIEWLET = 'div[id="workbench.view.debug"]'; const DEBUG_VIEW = `${VIEWLET}`; diff --git a/test/automation/src/driver.js b/test/automation/src/driver.js deleted file mode 100644 index c415029cdf9..00000000000 --- a/test/automation/src/driver.js +++ /dev/null @@ -1,16 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -//@ts-check -'use strict'; - -const path = require('path'); - -exports.connect = function (outPath, handle) { - const bootstrapPath = path.join(outPath, 'bootstrap-amd.js'); - const { load } = require(bootstrapPath); - - return new Promise((resolve, reject) => load('vs/platform/driver/node/driver', ({ connect }) => connect(handle).then(resolve, reject), reject)); -}; diff --git a/test/automation/src/electron.ts b/test/automation/src/electron.ts index efc8ff8da7d..42e46a60a0b 100644 --- a/test/automation/src/electron.ts +++ b/test/automation/src/electron.ts @@ -4,13 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { join } from 'path'; -import { platform } from 'os'; -import { tmpName } from 'tmp'; -import { connect as connectElectronDriver, IDisposable, IDriver } from './driver'; -import { ChildProcess, spawn, SpawnOptions } from 'child_process'; import * as mkdirp from 'mkdirp'; -import { promisify } from 'util'; -import { teardown } from './processes'; import { copyExtension } from './extensions'; import { URI } from 'vscode-uri'; import { measureAndLog } from './logger'; @@ -96,61 +90,6 @@ export async function resolveElectronConfiguration(options: LaunchOptions): Prom }; } -/** - * @deprecated should use the playwright based electron support instead - */ -export async function launch(options: LaunchOptions): Promise<{ electronProcess: ChildProcess; client: IDisposable; driver: IDriver }> { - const { codePath, logger, verbose } = options; - const { env, args, electronPath } = await resolveElectronConfiguration(options); - - const driverIPCHandle = await measureAndLog(createDriverHandle(), 'createDriverHandle', logger); - args.push('--driver', driverIPCHandle); - - const outPath = codePath ? getBuildOutPath(codePath) : getDevOutPath(); - - const spawnOptions: SpawnOptions = { env }; - - if (verbose) { - spawnOptions.stdio = ['ignore', 'inherit', 'inherit']; - } - - const electronProcess = spawn(electronPath, args, spawnOptions); - - logger.log(`Started electron for desktop smoke tests on pid ${electronProcess.pid}`); - - let retries = 0; - - while (true) { - try { - const { client, driver } = await measureAndLog(connectElectronDriver(outPath, driverIPCHandle), 'connectElectronDriver()', logger); - return { - electronProcess, - client, - driver - }; - } catch (err) { - - // give up - if (++retries > 30) { - logger.log(`Error connecting driver: ${err}. Giving up...`); - - await measureAndLog(teardown(electronProcess, logger), 'Kill Electron after failing to connect', logger); - - throw err; - } - - // retry - else { - if ((err as NodeJS.ErrnoException).code !== 'ENOENT' /* ENOENT is expected for as long as the server has not started on the socket */) { - logger.log(`Error connecting driver: ${err}. Attempting to retry...`); - } - - await new Promise(resolve => setTimeout(resolve, 1000)); - } - } - } -} - export function getDevElectronPath(): string { const buildPath = join(root, '.build'); const product = require(join(root, 'product.json')); @@ -192,28 +131,3 @@ export function getBuildVersion(root: string): string { return require(join(root, 'resources', 'app', 'package.json')).version; } } - -function getDevOutPath(): string { - return join(root, 'out'); -} - -function getBuildOutPath(root: string): string { - switch (process.platform) { - case 'darwin': - return join(root, 'Contents', 'Resources', 'app', 'out'); - default: - return join(root, 'resources', 'app', 'out'); - } -} - -async function createDriverHandle(): Promise { - - // Windows - if ('win32' === platform()) { - const name = [...Array(15)].map(() => Math.random().toString(36)[3]).join(''); - return `\\\\.\\pipe\\${name}`; - } - - // Posix - return promisify(tmpName)(); -} diff --git a/test/automation/src/index.ts b/test/automation/src/index.ts index e5ffa4e60cb..ba417bf1f27 100644 --- a/test/automation/src/index.ts +++ b/test/automation/src/index.ts @@ -25,5 +25,4 @@ export * from './terminal'; export * from './viewlet'; export * from './localization'; export * from './workbench'; -export * from './driver'; export { getDevElectronPath, getBuildElectronPath, getBuildVersion } from './electron'; diff --git a/test/automation/src/playwrightBrowser.ts b/test/automation/src/playwrightBrowser.ts index e594efe6688..5d85b0e910a 100644 --- a/test/automation/src/playwrightBrowser.ts +++ b/test/automation/src/playwrightBrowser.ts @@ -8,7 +8,6 @@ import { ChildProcess, spawn } from 'child_process'; import { join } from 'path'; import { mkdir } from 'fs'; import { promisify } from 'util'; -import { IDriver, IDisposable } from './driver'; import { URI } from 'vscode-uri'; import { Logger, measureAndLog } from './logger'; import type { LaunchOptions } from './code'; @@ -18,7 +17,7 @@ const root = join(__dirname, '..', '..', '..'); let port = 9000; -export async function launch(options: LaunchOptions): Promise<{ serverProcess: ChildProcess; client: IDisposable; driver: IDriver }> { +export async function launch(options: LaunchOptions): Promise<{ serverProcess: ChildProcess; driver: PlaywrightDriver }> { // Launch server const { serverProcess, endpoint } = await launchServer(options); @@ -28,9 +27,6 @@ export async function launch(options: LaunchOptions): Promise<{ serverProcess: C return { serverProcess, - client: { - dispose: () => { /* there is no client to dispose for browser, teardown is triggered via exitApplication call */ } - }, driver: new PlaywrightDriver(browser, context, page, serverProcess, options) }; } diff --git a/test/automation/src/playwrightDriver.ts b/test/automation/src/playwrightDriver.ts index 8ff2db7b1b3..61e219db4d5 100644 --- a/test/automation/src/playwrightDriver.ts +++ b/test/automation/src/playwrightDriver.ts @@ -5,14 +5,14 @@ import * as playwright from '@playwright/test'; import { join } from 'path'; -import { IDriver, IWindowDriver } from './driver'; +import { IWindowDriver } from './driver'; import { PageFunction } from 'playwright-core/types/structs'; import { measureAndLog } from './logger'; import { LaunchOptions } from './code'; import { teardown } from './processes'; import { ChildProcess } from 'child_process'; -export class PlaywrightDriver implements IDriver { +export class PlaywrightDriver { private static traceCounter = 1; private static screenShotCounter = 1; @@ -31,8 +31,6 @@ export class PlaywrightDriver implements IDriver { esc: 'Escape' }; - _serviceBrand: undefined; - constructor( private readonly application: playwright.Browser | playwright.ElectronApplication, private readonly context: playwright.BrowserContext, @@ -42,11 +40,7 @@ export class PlaywrightDriver implements IDriver { ) { } - async getWindowIds() { - return [1]; - } - - async startTracing(windowId: number, name: string): Promise { + async startTracing(name: string): Promise { if (!this.options.tracing) { return; // tracing disabled } @@ -58,7 +52,7 @@ export class PlaywrightDriver implements IDriver { } } - async stopTracing(windowId: number, name: string, persist: boolean): Promise { + async stopTracing(name: string, persist: boolean): Promise { if (!this.options.tracing) { return; // tracing disabled } @@ -120,7 +114,7 @@ export class PlaywrightDriver implements IDriver { // Desktop: exit via `driver.exitApplication` else { try { - await measureAndLog(this._evaluateWithDriver(([driver]) => driver.exitApplication()), 'driver.exitApplication()', this.options.logger); + await measureAndLog(this.evaluateWithDriver(([driver]) => driver.exitApplication()), 'driver.exitApplication()', this.options.logger); } catch (error) { this.options.logger.log(`Error exiting appliction (${error})`); } @@ -132,7 +126,7 @@ export class PlaywrightDriver implements IDriver { } } - async dispatchKeybinding(windowId: number, keybinding: string) { + async dispatchKeybinding(keybinding: string) { const chords = keybinding.split(' '); for (let i = 0; i < chords.length; i++) { const chord = chords[i]; @@ -162,60 +156,60 @@ export class PlaywrightDriver implements IDriver { await this.timeout(100); } - async click(windowId: number, selector: string, xoffset?: number | undefined, yoffset?: number | undefined) { - const { x, y } = await this.getElementXY(windowId, selector, xoffset, yoffset); + async click(selector: string, xoffset?: number | undefined, yoffset?: number | undefined) { + const { x, y } = await this.getElementXY(selector, xoffset, yoffset); await this.page.mouse.click(x + (xoffset ? xoffset : 0), y + (yoffset ? yoffset : 0)); } - async setValue(windowId: number, selector: string, text: string) { - return this.page.evaluate(([driver, selector, text]) => driver.setValue(selector, text), [await this._getDriverHandle(), selector, text] as const); + async setValue(selector: string, text: string) { + return this.page.evaluate(([driver, selector, text]) => driver.setValue(selector, text), [await this.getDriverHandle(), selector, text] as const); } - async getTitle(windowId: number) { - return this._evaluateWithDriver(([driver]) => driver.getTitle()); + async getTitle() { + return this.evaluateWithDriver(([driver]) => driver.getTitle()); } - async isActiveElement(windowId: number, selector: string) { - return this.page.evaluate(([driver, selector]) => driver.isActiveElement(selector), [await this._getDriverHandle(), selector] as const); + async isActiveElement(selector: string) { + return this.page.evaluate(([driver, selector]) => driver.isActiveElement(selector), [await this.getDriverHandle(), selector] as const); } - async getElements(windowId: number, selector: string, recursive: boolean = false) { - return this.page.evaluate(([driver, selector, recursive]) => driver.getElements(selector, recursive), [await this._getDriverHandle(), selector, recursive] as const); + async getElements(selector: string, recursive: boolean = false) { + return this.page.evaluate(([driver, selector, recursive]) => driver.getElements(selector, recursive), [await this.getDriverHandle(), selector, recursive] as const); } - async getElementXY(windowId: number, selector: string, xoffset?: number, yoffset?: number) { - return this.page.evaluate(([driver, selector, xoffset, yoffset]) => driver.getElementXY(selector, xoffset, yoffset), [await this._getDriverHandle(), selector, xoffset, yoffset] as const); + async getElementXY(selector: string, xoffset?: number, yoffset?: number) { + return this.page.evaluate(([driver, selector, xoffset, yoffset]) => driver.getElementXY(selector, xoffset, yoffset), [await this.getDriverHandle(), selector, xoffset, yoffset] as const); } - async typeInEditor(windowId: number, selector: string, text: string) { - return this.page.evaluate(([driver, selector, text]) => driver.typeInEditor(selector, text), [await this._getDriverHandle(), selector, text] as const); + async typeInEditor(selector: string, text: string) { + return this.page.evaluate(([driver, selector, text]) => driver.typeInEditor(selector, text), [await this.getDriverHandle(), selector, text] as const); } - async getTerminalBuffer(windowId: number, selector: string) { - return this.page.evaluate(([driver, selector]) => driver.getTerminalBuffer(selector), [await this._getDriverHandle(), selector] as const); + async getTerminalBuffer(selector: string) { + return this.page.evaluate(([driver, selector]) => driver.getTerminalBuffer(selector), [await this.getDriverHandle(), selector] as const); } - async writeInTerminal(windowId: number, selector: string, text: string) { - return this.page.evaluate(([driver, selector, text]) => driver.writeInTerminal(selector, text), [await this._getDriverHandle(), selector, text] as const); + async writeInTerminal(selector: string, text: string) { + return this.page.evaluate(([driver, selector, text]) => driver.writeInTerminal(selector, text), [await this.getDriverHandle(), selector, text] as const); } - async getLocaleInfo(windowId: number) { - return this._evaluateWithDriver(([driver]) => driver.getLocaleInfo()); + async getLocaleInfo() { + return this.evaluateWithDriver(([driver]) => driver.getLocaleInfo()); } - async getLocalizedStrings(windowId: number) { - return this._evaluateWithDriver(([driver]) => driver.getLocalizedStrings()); + async getLocalizedStrings() { + return this.evaluateWithDriver(([driver]) => driver.getLocalizedStrings()); } - private async _evaluateWithDriver(pageFunction: PageFunction[], T>) { - return this.page.evaluate(pageFunction, [await this._getDriverHandle()]); + private async evaluateWithDriver(pageFunction: PageFunction[], T>) { + return this.page.evaluate(pageFunction, [await this.getDriverHandle()]); } private timeout(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } - private async _getDriverHandle(): Promise> { + private async getDriverHandle(): Promise> { return this.page.evaluateHandle('window.driver'); } } diff --git a/test/automation/src/playwrightElectron.ts b/test/automation/src/playwrightElectron.ts index 1569841fdae..3ad47fbb2ec 100644 --- a/test/automation/src/playwrightElectron.ts +++ b/test/automation/src/playwrightElectron.ts @@ -4,14 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as playwright from '@playwright/test'; -import { IDriver, IDisposable } from './driver'; import type { LaunchOptions } from './code'; import { PlaywrightDriver } from './playwrightDriver'; import { IElectronConfiguration, resolveElectronConfiguration } from './electron'; import { measureAndLog } from './logger'; import { ChildProcess } from 'child_process'; -export async function launch(options: LaunchOptions): Promise<{ electronProcess: ChildProcess; client: IDisposable; driver: IDriver }> { +export async function launch(options: LaunchOptions): Promise<{ electronProcess: ChildProcess; driver: PlaywrightDriver }> { // Resolve electron config and update const { electronPath, args, env } = await resolveElectronConfiguration(options); @@ -23,9 +22,6 @@ export async function launch(options: LaunchOptions): Promise<{ electronProcess: return { electronProcess, - client: { - dispose: () => { /* there is no client to dispose for electron, teardown is triggered via exitApplication call */ } - }, driver: new PlaywrightDriver(electron, context, page, undefined /* no server process */, options) }; } diff --git a/test/automation/src/scm.ts b/test/automation/src/scm.ts index 60a33abb93f..7186fee17ca 100644 --- a/test/automation/src/scm.ts +++ b/test/automation/src/scm.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Viewlet } from './viewlet'; -import { IElement } from '../src/driver'; +import { IElement } from './driver'; import { findElement, findElements, Code } from './code'; const VIEWLET = 'div[id="workbench.view.scm"]'; diff --git a/test/automation/tools/copy-driver-definition.js b/test/automation/tools/copy-driver-definition.js index cf757ecbc38..36352008a47 100644 --- a/test/automation/tools/copy-driver-definition.js +++ b/test/automation/tools/copy-driver-definition.js @@ -21,34 +21,14 @@ contents = `/*------------------------------------------------------------------ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/** - * Thenable is a common denominator between ES6 promises, Q, jquery.Deferred, WinJS.Promise, - * and others. This API makes no assumption about what promise library is being used which - * enables reusing existing code without migrating to a specific promise implementation. Still, - * we recommend the use of native promises which are available in this editor. - */ -interface Thenable { - /** - * Attaches callbacks for the resolution and/or rejection of the Promise. - * @param onfulfilled The callback to execute when the Promise is resolved. - * @param onrejected The callback to execute when the Promise is rejected. - * @returns A Promise for the completion of which ever callback is executed. - */ - then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => TResult | Thenable): Thenable; - then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => void): Thenable; -} - ${contents} - -export interface IDisposable { - dispose(): void; -} - -export function connect(outPath: string, handle: string): Promise<{ client: IDisposable, driver: IDriver }>; `; const srcPath = path.join(path.dirname(__dirname), 'src'); const outPath = path.join(path.dirname(__dirname), 'out'); +if (!fs.existsSync(outPath)) { + fs.mkdirSync(outPath); +} fs.writeFileSync(path.join(srcPath, 'driver.d.ts'), contents); fs.writeFileSync(path.join(outPath, 'driver.d.ts'), contents); diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index a1226638fc7..7f5accccec7 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -45,7 +45,6 @@ const opts = minimist(args, { 'remote', 'web', 'headless', - 'legacy', 'tracing' ], default: { @@ -56,7 +55,6 @@ const opts = minimist(args, { remote?: boolean; headless?: boolean; web?: boolean; - legacy?: boolean; tracing?: boolean; build?: string; 'stable-build'?: string; @@ -71,9 +69,9 @@ const logsRootPath = (() => { if (opts.web) { logsName = 'smoke-tests-browser'; } else if (opts.remote) { - logsName = opts.legacy ? 'smoke-tests-remote-legacy' : 'smoke-tests-remote'; + logsName = 'smoke-tests-remote'; } else { - logsName = opts.legacy ? 'smoke-tests-electron-legacy' : 'smoke-tests-electron'; + logsName = 'smoke-tests-electron'; } return path.join(logsParentPath, logsName); @@ -326,13 +324,11 @@ before(async function () { workspacePath, userDataDir, extensionsPath, - waitTime: parseInt(opts['wait-time'] || '0') || 20, logger, logsPath: path.join(logsRootPath, 'suite_unknown'), verbose: opts.verbose, remote: opts.remote, web: opts.web, - legacy: opts.legacy, tracing: opts.tracing, headless: opts.headless, browser: opts.browser, @@ -366,7 +362,7 @@ after(async function () { } }); -describe(`VSCode Smoke Tests (${opts.web ? 'Web' : opts.legacy ? 'Electron (legacy)' : 'Electron'})`, () => { +describe(`VSCode Smoke Tests (${opts.web ? 'Web' : 'Electron'})`, () => { if (!opts.web) { setupDataLossTests(() => opts['stable-build'] /* Do not change, deferred for a reason! */, logger); } setupPreferencesTests(logger); setupSearchTests(logger); diff --git a/test/smoke/test/index.js b/test/smoke/test/index.js index 7b2894db1e1..8e9646c0d65 100644 --- a/test/smoke/test/index.js +++ b/test/smoke/test/index.js @@ -12,11 +12,11 @@ const minimist = require('minimist'); const [, , ...args] = process.argv; const opts = minimist(args, { - boolean: ['web', 'legacy'], + boolean: ['web'], string: ['f', 'g'] }); -const suite = opts['web'] ? 'Browser Smoke Tests' : opts['legacy'] ? 'Desktop Smoke Tests (Legacy)' : 'Desktop Smoke Tests'; +const suite = opts['web'] ? 'Browser Smoke Tests' : 'Desktop Smoke Tests'; const options = { color: true, From 82d8e0f018b20cac98983440042233533ac82922 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 13 Apr 2022 10:27:56 -0700 Subject: [PATCH 048/245] web: add telemetry api for embedders (#146931) * web: add telemetry api for embedders * fixup! expose just the telemetry level instead of publicLog * fixup! move telemetry level under env * fixup! pr comments * fixup! build --- .../standalone/browser/standaloneServices.ts | 3 ++- src/vs/platform/telemetry/common/telemetry.ts | 3 ++- .../telemetry/common/telemetryService.ts | 16 +++++++-------- .../telemetry/common/telemetryUtils.ts | 3 ++- .../test/browser/telemetryService.test.ts | 6 +++--- .../api/browser/mainThreadTelemetry.ts | 20 ++++++++----------- src/vs/workbench/browser/web.api.ts | 7 +++++++ src/vs/workbench/browser/web.factory.ts | 5 +++++ src/vs/workbench/browser/web.main.ts | 5 ++++- .../contrib/debug/browser/debugSession.ts | 2 +- .../textsearch.perf.integrationTest.ts | 3 ++- .../tags/electron-sandbox/workspaceTags.ts | 2 +- .../telemetry/browser/telemetryService.ts | 3 ++- .../electron-sandbox/telemetryService.ts | 3 ++- 14 files changed, 48 insertions(+), 33 deletions(-) diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index 3e53572dd10..81a9950b5e4 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -85,6 +85,7 @@ import { MarkerService } from 'vs/platform/markers/common/markerService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage'; +import { staticObservableValue } from 'vs/base/common/observableValue'; import 'vs/editor/common/services/languageFeaturesService'; @@ -640,7 +641,7 @@ class StandaloneResourcePropertiesService implements ITextResourcePropertiesServ class StandaloneTelemetryService implements ITelemetryService { declare readonly _serviceBrand: undefined; - public telemetryLevel = TelemetryLevel.NONE; + public telemetryLevel = staticObservableValue(TelemetryLevel.NONE); public sendErrorTelemetry = false; public setEnabled(value: boolean): void { diff --git a/src/vs/platform/telemetry/common/telemetry.ts b/src/vs/platform/telemetry/common/telemetry.ts index be381cd790c..1577949b1e2 100644 --- a/src/vs/platform/telemetry/common/telemetry.ts +++ b/src/vs/platform/telemetry/common/telemetry.ts @@ -5,6 +5,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings'; +import { IObservableValue } from 'vs/base/common/observableValue'; export const ITelemetryService = createDecorator('telemetryService'); @@ -46,7 +47,7 @@ export interface ITelemetryService { setExperimentProperty(name: string, value: string): void; - telemetryLevel: TelemetryLevel; + readonly telemetryLevel: IObservableValue; } export interface ITelemetryEndpoint { diff --git a/src/vs/platform/telemetry/common/telemetryService.ts b/src/vs/platform/telemetry/common/telemetryService.ts index f16e3f0d240..e6ad0d44982 100644 --- a/src/vs/platform/telemetry/common/telemetryService.ts +++ b/src/vs/platform/telemetry/common/telemetryService.ts @@ -5,6 +5,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { cloneAndChange, mixin } from 'vs/base/common/objects'; +import { MutableObservableValue } from 'vs/base/common/observableValue'; import { isWeb } from 'vs/base/common/platform'; import { escapeRegExpCharacters } from 'vs/base/common/strings'; import { localize } from 'vs/nls'; @@ -35,9 +36,10 @@ export class TelemetryService implements ITelemetryService { private _commonProperties: Promise<{ [name: string]: any }>; private _experimentProperties: { [name: string]: string } = {}; private _piiPaths: string[]; - private _telemetryLevel: TelemetryLevel; private _sendErrorTelemetry: boolean; + public readonly telemetryLevel = new MutableObservableValue(TelemetryLevel.USAGE); + private readonly _disposables = new DisposableStore(); private _cleanupPatterns: RegExp[] = []; @@ -49,7 +51,6 @@ export class TelemetryService implements ITelemetryService { this._appenders = config.appenders; this._commonProperties = config.commonProperties || Promise.resolve({}); this._piiPaths = config.piiPaths || []; - this._telemetryLevel = TelemetryLevel.USAGE; this._sendErrorTelemetry = !!config.sendErrorTelemetry; // static cleanup pattern for: `file:///DANGEROUS/PATH/resources/app/Useful/Information` @@ -59,7 +60,6 @@ export class TelemetryService implements ITelemetryService { this._cleanupPatterns.push(new RegExp(escapeRegExpCharacters(piiPath), 'gi')); } - this._updateTelemetryLevel(); this._configurationService.onDidChangeConfiguration(this._updateTelemetryLevel, this, this._disposables); } @@ -69,19 +69,17 @@ export class TelemetryService implements ITelemetryService { } private _updateTelemetryLevel(): void { - this._telemetryLevel = getTelemetryLevel(this._configurationService); + let level = getTelemetryLevel(this._configurationService); const collectableTelemetry = this._productService.enabledTelemetryLevels; // Also ensure that error telemetry is respecting the product configuration for collectable telemetry if (collectableTelemetry) { this._sendErrorTelemetry = this.sendErrorTelemetry ? collectableTelemetry.error : false; // Make sure the telemetry level from the service is the minimum of the config and product const maxCollectableTelemetryLevel = collectableTelemetry.usage ? TelemetryLevel.USAGE : collectableTelemetry.error ? TelemetryLevel.ERROR : TelemetryLevel.NONE; - this._telemetryLevel = Math.min(this._telemetryLevel, maxCollectableTelemetryLevel); + level = Math.min(level, maxCollectableTelemetryLevel); } - } - get telemetryLevel(): TelemetryLevel { - return this._telemetryLevel; + this.telemetryLevel.value = level; } get sendErrorTelemetry(): boolean { @@ -106,7 +104,7 @@ export class TelemetryService implements ITelemetryService { private _log(eventName: string, eventLevel: TelemetryLevel, data?: ITelemetryData, anonymizeFilePaths?: boolean): Promise { // don't send events when the user is optout - if (this.telemetryLevel < eventLevel) { + if (this.telemetryLevel.value < eventLevel) { return Promise.resolve(undefined); } diff --git a/src/vs/platform/telemetry/common/telemetryUtils.ts b/src/vs/platform/telemetry/common/telemetryUtils.ts index d74b997e1dd..45757fec68b 100644 --- a/src/vs/platform/telemetry/common/telemetryUtils.ts +++ b/src/vs/platform/telemetry/common/telemetryUtils.ts @@ -5,6 +5,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { safeStringify } from 'vs/base/common/objects'; +import { staticObservableValue } from 'vs/base/common/observableValue'; import { isObject } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { ConfigurationTarget, ConfigurationTargetToString, IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -31,7 +32,7 @@ export class NullTelemetryServiceShape implements ITelemetryService { } setExperimentProperty() { } - telemetryLevel = TelemetryLevel.NONE; + telemetryLevel = staticObservableValue(TelemetryLevel.NONE); getTelemetryInfo(): Promise { return Promise.resolve({ instanceId: 'someValue.instanceId', diff --git a/src/vs/platform/telemetry/test/browser/telemetryService.test.ts b/src/vs/platform/telemetry/test/browser/telemetryService.test.ts index 9fbe21f1057..808db0e4a82 100644 --- a/src/vs/platform/telemetry/test/browser/telemetryService.test.ts +++ b/src/vs/platform/telemetry/test/browser/telemetryService.test.ts @@ -800,15 +800,15 @@ suite('TelemetryService', () => { } }(), TestProductService); - assert.strictEqual(service.telemetryLevel, TelemetryLevel.NONE); + assert.strictEqual(service.telemetryLevel.value, TelemetryLevel.NONE); telemetryLevel = TelemetryConfiguration.ON; emitter.fire({}); - assert.strictEqual(service.telemetryLevel, TelemetryLevel.USAGE); + assert.strictEqual(service.telemetryLevel.value, TelemetryLevel.USAGE); telemetryLevel = TelemetryConfiguration.ERROR; emitter.fire({}); - assert.strictEqual(service.telemetryLevel, TelemetryLevel.ERROR); + assert.strictEqual(service.telemetryLevel.value, TelemetryLevel.ERROR); service.dispose(); }); diff --git a/src/vs/workbench/api/browser/mainThreadTelemetry.ts b/src/vs/workbench/api/browser/mainThreadTelemetry.ts index 6e13fa13053..ac89aca9280 100644 --- a/src/vs/workbench/api/browser/mainThreadTelemetry.ts +++ b/src/vs/workbench/api/browser/mainThreadTelemetry.ts @@ -3,15 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ITelemetryService, TelemetryLevel, TELEMETRY_OLD_SETTING_ID, TELEMETRY_SETTING_ID } from 'vs/platform/telemetry/common/telemetry'; -import { MainThreadTelemetryShape, MainContext, ExtHostTelemetryShape, ExtHostContext } from '../common/extHost.protocol'; -import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; -import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IProductService } from 'vs/platform/product/common/productService'; -import { getTelemetryLevel, supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; +import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings'; +import { ITelemetryService, TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; +import { supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; +import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; +import { ExtHostContext, ExtHostTelemetryShape, MainContext, MainThreadTelemetryShape } from '../common/extHost.protocol'; @extHostNamedCustomer(MainContext.MainThreadTelemetry) export class MainThreadTelemetry extends Disposable implements MainThreadTelemetryShape { @@ -22,7 +21,6 @@ export class MainThreadTelemetry extends Disposable implements MainThreadTelemet constructor( extHostContext: IExtHostContext, @ITelemetryService private readonly _telemetryService: ITelemetryService, - @IConfigurationService private readonly _configurationService: IConfigurationService, @IEnvironmentService private readonly _environmentService: IEnvironmentService, @IProductService private readonly _productService: IProductService ) { @@ -31,10 +29,8 @@ export class MainThreadTelemetry extends Disposable implements MainThreadTelemet this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTelemetry); if (supportsTelemetry(this._productService, this._environmentService)) { - this._register(this._configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(TELEMETRY_SETTING_ID) || e.affectsConfiguration(TELEMETRY_OLD_SETTING_ID)) { - this._proxy.$onDidChangeTelemetryLevel(this.telemetryLevel); - } + this._register(_telemetryService.telemetryLevel.onDidChange(level => { + this._proxy.$onDidChangeTelemetryLevel(level); })); } @@ -46,7 +42,7 @@ export class MainThreadTelemetry extends Disposable implements MainThreadTelemet return TelemetryLevel.NONE; } - return getTelemetryLevel(this._configurationService); + return this._telemetryService.telemetryLevel.value; } $publicLog(eventName: string, data: any = Object.create(null)): void { diff --git a/src/vs/workbench/browser/web.api.ts b/src/vs/workbench/browser/web.api.ts index 644e3c3f3af..35af583e718 100644 --- a/src/vs/workbench/browser/web.api.ts +++ b/src/vs/workbench/browser/web.api.ts @@ -15,6 +15,8 @@ import type { IProductConfiguration } from 'vs/base/common/product'; import type { ICredentialsProvider } from 'vs/platform/credentials/common/credentials'; import type { TunnelProviderFeatures } from 'vs/platform/tunnel/common/tunnel'; import type { IProgress, IProgressCompositeOptions, IProgressDialogOptions, IProgressNotificationOptions, IProgressOptions, IProgressStep, IProgressWindowOptions } from 'vs/platform/progress/common/progress'; +import { IObservableValue } from 'vs/base/common/observableValue'; +import { TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; /** * The `IWorkbench` interface is the API facade for web embedders @@ -62,6 +64,11 @@ export interface IWorkbench { * workbench. */ openUri(target: URI): Promise; + + /** + * Current workbench telemetry level. + */ + readonly telemetryLevel: IObservableValue; }; window: { diff --git a/src/vs/workbench/browser/web.factory.ts b/src/vs/workbench/browser/web.factory.ts index 8eb57787353..cc1945ea86c 100644 --- a/src/vs/workbench/browser/web.factory.ts +++ b/src/vs/workbench/browser/web.factory.ts @@ -13,6 +13,8 @@ import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { DeferredPromise } from 'vs/base/common/async'; import { asArray } from 'vs/base/common/arrays'; import { IProgress, IProgressCompositeOptions, IProgressDialogOptions, IProgressNotificationOptions, IProgressOptions, IProgressStep, IProgressWindowOptions } from 'vs/platform/progress/common/progress'; +import { IObservableValue } from 'vs/base/common/observableValue'; +import { TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; let created = false; const workbenchPromise = new DeferredPromise(); @@ -120,6 +122,9 @@ export namespace env { return workbench.env.openUri(target); } + + export const telemetryLevel: Promise> = + workbenchPromise.p.then(workbench => workbench.env.telemetryLevel); } export namespace window { diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index c173f780471..8e486898b4f 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -69,6 +69,7 @@ import { IndexedDB } from 'vs/base/browser/indexedDB'; import { BrowserCredentialsService } from 'vs/workbench/services/credentials/browser/credentialsService'; import { IWorkspace } from 'vs/workbench/services/host/browser/browserHostService'; import { WebFileSystemAccess } from 'vs/platform/files/browser/webFileSystemAccess'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IProgressService } from 'vs/platform/progress/common/progress'; export class BrowserMain extends Disposable { @@ -117,6 +118,7 @@ export class BrowserMain extends Disposable { const timerService = accessor.get(ITimerService); const openerService = accessor.get(IOpenerService); const productService = accessor.get(IProductService); + const telemetryService = accessor.get(ITelemetryService); const progessService = accessor.get(IProgressService); return { @@ -132,7 +134,8 @@ export class BrowserMain extends Disposable { }, async openUri(uri: URI): Promise { return openerService.open(uri, {}); - } + }, + telemetryLevel: telemetryService.telemetryLevel, }, window: { withProgress: (options, task) => progessService.withProgress(options, task) diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 9109c4f0145..0bd109a80d9 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -1071,7 +1071,7 @@ export class DebugSession implements IDebugSession { // only log telemetry events from debug adapter if the debug extension provided the telemetry key // and the user opted in telemetry const telemetryEndpoint = this.raw.dbgr.getCustomTelemetryEndpoint(); - if (telemetryEndpoint && this.telemetryService.telemetryLevel !== TelemetryLevel.NONE) { + if (telemetryEndpoint && this.telemetryService.telemetryLevel.value !== TelemetryLevel.NONE) { // __GDPR__TODO__ We're sending events in the name of the debug extension and we can not ensure that those are declared correctly. let data = event.body.data; if (!telemetryEndpoint.sendErrorTelemetry && event.body.data) { diff --git a/src/vs/workbench/contrib/search/test/electron-browser/textsearch.perf.integrationTest.ts b/src/vs/workbench/contrib/search/test/electron-browser/textsearch.perf.integrationTest.ts index d1a74339e79..cfcbec62d50 100644 --- a/src/vs/workbench/contrib/search/test/electron-browser/textsearch.perf.integrationTest.ts +++ b/src/vs/workbench/contrib/search/test/electron-browser/textsearch.perf.integrationTest.ts @@ -43,6 +43,7 @@ import { TestContextService, TestTextResourcePropertiesService } from 'vs/workbe import { TestEnvironmentService } from 'vs/workbench/test/electron-browser/workbenchTestServices'; import { LanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce'; import { LanguageFeaturesService } from 'vs/editor/common/services/languageFeaturesService'; +import { staticObservableValue } from 'vs/base/common/observableValue'; // declare var __dirname: string; @@ -182,7 +183,7 @@ suite.skip('TextSearch performance (integration)', () => { class TestTelemetryService implements ITelemetryService { public _serviceBrand: undefined; - public telemetryLevel = TelemetryLevel.USAGE; + public telemetryLevel = staticObservableValue(TelemetryLevel.USAGE); public sendErrorTelemetry = true; public events: any[] = []; diff --git a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTags.ts b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTags.ts index 395145ee8b4..8e263a87401 100644 --- a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTags.ts +++ b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTags.ts @@ -36,7 +36,7 @@ export class WorkspaceTags implements IWorkbenchContribution { @IProductService private readonly productService: IProductService, @INativeHostService private readonly nativeHostService: INativeHostService ) { - if (this.telemetryService.telemetryLevel === TelemetryLevel.USAGE) { + if (this.telemetryService.telemetryLevel.value === TelemetryLevel.USAGE) { this.report(); } } diff --git a/src/vs/workbench/services/telemetry/browser/telemetryService.ts b/src/vs/workbench/services/telemetry/browser/telemetryService.ts index 448f2aa30b9..4b4d5ead47c 100644 --- a/src/vs/workbench/services/telemetry/browser/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/browser/telemetryService.ts @@ -5,6 +5,7 @@ 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'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILoggerService } from 'vs/platform/log/common/log'; @@ -138,7 +139,7 @@ export class TelemetryService extends Disposable implements ITelemetryService { return this.impl.setExperimentProperty(name, value); } - get telemetryLevel(): TelemetryLevel { + get telemetryLevel(): IObservableValue { return this.impl.telemetryLevel; } diff --git a/src/vs/workbench/services/telemetry/electron-sandbox/telemetryService.ts b/src/vs/workbench/services/telemetry/electron-sandbox/telemetryService.ts index ca23c70a78f..a91c4a23be9 100644 --- a/src/vs/workbench/services/telemetry/electron-sandbox/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/electron-sandbox/telemetryService.ts @@ -17,6 +17,7 @@ import { TelemetryService as BaseTelemetryService, ITelemetryServiceConfig } fro import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings'; import { IFileService } from 'vs/platform/files/common/files'; +import { IObservableValue } from 'vs/base/common/observableValue'; export class TelemetryService extends Disposable implements ITelemetryService { @@ -56,7 +57,7 @@ export class TelemetryService extends Disposable implements ITelemetryService { return this.impl.setExperimentProperty(name, value); } - get telemetryLevel(): TelemetryLevel { + get telemetryLevel(): IObservableValue { return this.impl.telemetryLevel; } From e9c551906f44c50eef1e0246c3d9b876f4ed4084 Mon Sep 17 00:00:00 2001 From: Aman Khalid Date: Wed, 13 Apr 2022 13:41:36 -0400 Subject: [PATCH 049/245] testing: UI filter survives window reload * Close #143241: Testing UI filter survives window reload * Apply suggestions from review of PR #147344 Co-authored-by: Connor Peet Co-authored-by: Connor Peet --- .../contrib/testing/browser/testingExplorerFilter.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts index c32661787e3..4d1af363aa7 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts @@ -65,6 +65,11 @@ export class TestingExplorerFilter extends BaseActionViewItem { const wrapper = this.wrapper = dom.$('.testing-filter-wrapper'); container.appendChild(wrapper); + const history = this.history.get([]); + if (history.length) { + this.state.setText(history[history.length - 1]); + } + const input = this.input = this._register(this.instantiationService.createInstance(ContextScopedSuggestEnabledInputWithHistory, { id: 'testing.explorer.filter', ariaLabel: localize('testExplorerFilterLabel', "Filter text for tests in the explorer"), @@ -89,7 +94,7 @@ export class TestingExplorerFilter extends BaseActionViewItem { value: this.state.text.value, placeholderText: localize('testExplorerFilter', "Filter (e.g. text, !exclude, @tag)"), }, - history: this.history.get([]) + history })); this._register(attachSuggestEnabledInputBoxStyler(input, this.themeService)); From 5f9e98878e327fa7f77dd9ba4c28e872dcdbc3f4 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Wed, 13 Apr 2022 10:42:51 -0700 Subject: [PATCH 050/245] Better fix for #145996 --- src/vs/workbench/contrib/search/browser/searchView.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 7cec0e4491a..a587b74597b 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -8,7 +8,6 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; import { IIdentityProvider } from 'vs/base/browser/ui/list/list'; -import { Orientation } from 'vs/base/browser/ui/sash/sash'; import { ITreeContextMenuEvent, ITreeElement } from 'vs/base/browser/ui/tree/tree'; import { IAction } from 'vs/base/common/actions'; import { Delayer } from 'vs/base/common/async'; @@ -1065,10 +1064,10 @@ export class SearchView extends ViewPane { this.inputPatternExcludes.setWidth(this.size.width - 28 /* container margin */); this.inputPatternIncludes.setWidth(this.size.width - 28 /* container margin */); - const widgetHeight = dom.getTotalHeight(this.searchWidget.domNode); + + const widgetHeight = dom.getTotalHeight(this.searchWidgetsContainerElement); const messagesHeight = dom.getTotalHeight(this.messagesElement); - const margin = 25; - this.tree.layout(this.size.height - widgetHeight - messagesHeight - margin, this.size.width - 28); + this.tree.layout(this.size.height - widgetHeight - messagesHeight, this.size.width - 28); } protected override layoutBody(height: number, width: number): void { @@ -1285,7 +1284,7 @@ export class SearchView extends ViewPane { } if (!skipLayout && this.size) { - this.layout(this._orientation === Orientation.VERTICAL ? this.size.height : this.size.width); + this.reLayout(); } } From 4e697d42086b7535f90deecce642d4bc7fd8b0e7 Mon Sep 17 00:00:00 2001 From: aamunger Date: Wed, 13 Apr 2022 10:51:23 -0700 Subject: [PATCH 051/245] update output style when font family changes --- .../notebook/browser/notebookEditorWidget.ts | 6 +++++- .../contrib/notebook/common/notebookOptions.ts | 14 ++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 93bcb157f61..40712d1eaa3 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -397,7 +397,11 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD this._updateForNotebookConfiguration(); } - if (e.compactView || e.focusIndicator || e.insertToolbarPosition || e.cellToolbarLocation || e.dragAndDropEnabled || e.fontSize || e.outputFontSize || e.markupFontSize || e.insertToolbarAlignment || e.outputLineHeight) { + if (e.fontFamily) { + this._generateFontInfo(); + } + + if (e.compactView || e.focusIndicator || e.insertToolbarPosition || e.cellToolbarLocation || e.dragAndDropEnabled || e.fontSize || e.outputFontSize || e.markupFontSize || e.fontFamily || e.outputFontFamily || e.insertToolbarAlignment || e.outputLineHeight) { this._styleElement?.remove(); this._createLayoutStyles(); this._webview?.updateOptions({ diff --git a/src/vs/workbench/contrib/notebook/common/notebookOptions.ts b/src/vs/workbench/contrib/notebook/common/notebookOptions.ts index 2964a52df79..426871f84fa 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookOptions.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookOptions.ts @@ -89,6 +89,8 @@ export interface NotebookOptionsChangeEvent { readonly fontSize?: boolean; readonly outputFontSize?: boolean; readonly markupFontSize?: boolean; + readonly fontFamily?: boolean; + readonly outputFontFamily?: boolean; readonly editorOptionsCustomizations?: boolean; readonly interactiveWindowCollapseCodeCells?: boolean; readonly outputLineHeight?: boolean; @@ -235,11 +237,12 @@ export class NotebookOptions extends Disposable { const fontSize = e.affectsConfiguration('editor.fontSize'); const outputFontSize = e.affectsConfiguration(NotebookSetting.outputFontSize); const markupFontSize = e.affectsConfiguration(NotebookSetting.markupFontSize); + const fontFamily = e.affectsConfiguration('editor.fontFamily'); + const outputFontFamily = e.affectsConfiguration(NotebookSetting.outputFontFamily); const editorOptionsCustomizations = e.affectsConfiguration(NotebookSetting.cellEditorOptionsCustomizations); const interactiveWindowCollapseCodeCells = e.affectsConfiguration(NotebookSetting.interactiveWindowCollapseCodeCells); const outputLineHeight = e.affectsConfiguration(NotebookSetting.outputLineHeight); - if ( !cellStatusBarVisibility && !cellToolbarLocation @@ -256,6 +259,8 @@ export class NotebookOptions extends Disposable { && !fontSize && !outputFontSize && !markupFontSize + && !fontFamily + && !outputFontFamily && !editorOptionsCustomizations && !interactiveWindowCollapseCodeCells && !outputLineHeight) { @@ -328,6 +333,10 @@ export class NotebookOptions extends Disposable { configuration.markupFontSize = this.configurationService.getValue(NotebookSetting.markupFontSize); } + if (outputFontFamily) { + configuration.outputFontFamily = this.configurationService.getValue(NotebookSetting.outputFontFamily); + } + if (editorOptionsCustomizations) { configuration.editorOptionsCustomizations = this.configurationService.getValue(NotebookSetting.cellEditorOptionsCustomizations); } @@ -342,7 +351,6 @@ export class NotebookOptions extends Disposable { this._layoutConfiguration = Object.freeze(configuration); - // add new configuration to the event? // trigger event this._onDidChangeOptions.fire({ cellStatusBarVisibility, @@ -360,6 +368,8 @@ export class NotebookOptions extends Disposable { fontSize, outputFontSize, markupFontSize, + fontFamily, + outputFontFamily, editorOptionsCustomizations, interactiveWindowCollapseCodeCells, outputLineHeight From 30823fcbb4e176367cdb16a81fc7d31f55f027a7 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 13 Apr 2022 20:38:30 +0200 Subject: [PATCH 052/245] build: split darwin universal --- .../darwin/product-build-darwin-universal.yml | 95 +++++++++++++++++++ .../darwin/product-build-darwin.yml | 41 +------- build/azure-pipelines/product-build.yml | 4 +- 3 files changed, 102 insertions(+), 38 deletions(-) create mode 100644 build/azure-pipelines/darwin/product-build-darwin-universal.yml diff --git a/build/azure-pipelines/darwin/product-build-darwin-universal.yml b/build/azure-pipelines/darwin/product-build-darwin-universal.yml new file mode 100644 index 00000000000..1b8cfef6737 --- /dev/null +++ b/build/azure-pipelines/darwin/product-build-darwin-universal.yml @@ -0,0 +1,95 @@ +steps: + - task: NodeTool@0 + inputs: + versionSpec: "16.x" + + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode + SecretsFilter: "github-distro-mixin-password,macos-developer-certificate,macos-developer-certificate-key" + + - script: | + set -e + cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF + + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + displayName: Prepare tooling + + - script: | + set -e + git fetch https://github.com/$(VSCODE_MIXIN_REPO).git $VSCODE_DISTRO_REF + echo "##vso[task.setvariable variable=VSCODE_DISTRO_COMMIT;]$(git rev-parse FETCH_HEAD)" + git checkout FETCH_HEAD + condition: and(succeeded(), ne(variables.VSCODE_DISTRO_REF, ' ')) + displayName: Checkout override commit + + - script: | + set -e + git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") + displayName: Merge distro + + - script: | + mkdir -p .build + node build/azure-pipelines/common/computeNodeModulesCacheKey.js x64 $ENABLE_TERRAPIN > .build/yarnlockhash + displayName: Prepare yarn cache flags + + - task: Cache@2 + inputs: + key: "nodeModules | $(Agent.OS) | .build/yarnlockhash" + path: .build/node_modules_cache + cacheHitVar: NODE_MODULES_RESTORED + displayName: Restore node_modules cache + + - script: | + set -e + tar -xzf .build/node_modules_cache/cache.tgz + displayName: Extract node_modules cache + + - script: | + set -e + node build/azure-pipelines/mixin + displayName: Mix in quality + + - download: current + artifact: unsigned_vscode_client_darwin_x64_archive + displayName: Download x64 artifact + + - download: current + artifact: unsigned_vscode_client_darwin_arm64_archive + displayName: Download arm64 artifact + + - script: | + set -e + cp $(Pipeline.Workspace)/unsigned_vscode_client_darwin_x64_archive/VSCode-darwin-x64.zip $(agent.builddirectory)/VSCode-darwin-x64.zip + cp $(Pipeline.Workspace)/unsigned_vscode_client_darwin_arm64_archive/VSCode-darwin-arm64.zip $(agent.builddirectory)/VSCode-darwin-arm64.zip + unzip $(agent.builddirectory)/VSCode-darwin-x64.zip -d $(agent.builddirectory)/VSCode-darwin-x64 + unzip $(agent.builddirectory)/VSCode-darwin-arm64.zip -d $(agent.builddirectory)/VSCode-darwin-arm64 + DEBUG=* node build/darwin/create-universal-app.js + displayName: Create Universal App + + - script: | + set -e + security create-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain + security default-keychain -s $(agent.tempdirectory)/buildagent.keychain + security unlock-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain + echo "$(macos-developer-certificate)" | base64 -D > $(agent.tempdirectory)/cert.p12 + security import $(agent.tempdirectory)/cert.p12 -k $(agent.tempdirectory)/buildagent.keychain -P "$(macos-developer-certificate-key)" -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k pwd $(agent.tempdirectory)/buildagent.keychain + VSCODE_ARCH=$(VSCODE_ARCH) DEBUG=electron-osx-sign* node build/darwin/sign.js + displayName: Set Hardened Entitlements + + - script: | + set -e + pushd $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) && zip -r -X -y $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH).zip * && popd + displayName: Archive build + + - publish: $(Agent.BuildDirectory)/VSCode-darwin-$(VSCODE_ARCH).zip + artifact: unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive + displayName: Publish client archive diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 073932d7032..c94a51b3f73 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -15,16 +15,12 @@ steps: artifact: Compilation path: $(Build.ArtifactStagingDirectory) displayName: Download compilation output - condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'universal')) - script: | set -e tar -xzf $(Build.ArtifactStagingDirectory)/compilation.tar.gz displayName: Extract compilation output - condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'universal')) - # Set up the credentials to retrieve distro repo and setup git persona - # to create a merge commit for when we merge distro into oss - script: | set -e cat << EOF > ~/.netrc @@ -122,7 +118,6 @@ steps: VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ yarn gulp vscode-darwin-$(VSCODE_ARCH)-min-ci displayName: Build client - condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'universal')) - script: | set -e @@ -136,34 +131,13 @@ steps: VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ yarn gulp vscode-reh-web-darwin-$(VSCODE_ARCH)-min-ci displayName: Build Server - condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'universal')) - script: | set -e VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ yarn npm-run-all -lp "electron $(VSCODE_ARCH)" "playwright-install" displayName: Download Electron and Playwright - condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'universal'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - - download: current - artifact: unsigned_vscode_client_darwin_x64_archive - displayName: Download x64 artifact - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'universal')) - - - download: current - artifact: unsigned_vscode_client_darwin_arm64_archive - displayName: Download arm64 artifact - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'universal')) - - - script: | - set -e - cp $(Pipeline.Workspace)/unsigned_vscode_client_darwin_x64_archive/VSCode-darwin-x64.zip $(agent.builddirectory)/VSCode-darwin-x64.zip - cp $(Pipeline.Workspace)/unsigned_vscode_client_darwin_arm64_archive/VSCode-darwin-arm64.zip $(agent.builddirectory)/VSCode-darwin-arm64.zip - unzip $(agent.builddirectory)/VSCode-darwin-x64.zip -d $(agent.builddirectory)/VSCode-darwin-x64 - unzip $(agent.builddirectory)/VSCode-darwin-arm64.zip -d $(agent.builddirectory)/VSCode-darwin-arm64 - DEBUG=* node build/darwin/create-universal-app.js - displayName: Create Universal App - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'universal')) + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) # Setting hardened entitlements is a requirement for: # * Apple notarization @@ -311,7 +285,6 @@ steps: set -e pushd $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) && zip -r -X -y $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH).zip * && popd displayName: Archive build - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - script: | set -e @@ -322,22 +295,18 @@ steps: # package Remote Extension Host (Web) pushd .. && mv vscode-reh-web-darwin-$(VSCODE_ARCH) vscode-server-darwin-$(VSCODE_ARCH)-web && zip -Xry vscode-server-darwin-$(VSCODE_ARCH)-web.zip vscode-server-darwin-$(VSCODE_ARCH)-web && popd displayName: Prepare to publish servers - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false'), ne(variables['VSCODE_ARCH'], 'universal')) - publish: $(Agent.BuildDirectory)/VSCode-darwin-$(VSCODE_ARCH).zip artifact: unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive displayName: Publish client archive - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - publish: $(Agent.BuildDirectory)/vscode-server-darwin-$(VSCODE_ARCH).zip artifact: vscode_server_darwin_$(VSCODE_ARCH)_archive-unsigned displayName: Publish server archive - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false'), ne(variables['VSCODE_ARCH'], 'universal')) - publish: $(Agent.BuildDirectory)/vscode-server-darwin-$(VSCODE_ARCH)-web.zip artifact: vscode_web_darwin_$(VSCODE_ARCH)_archive-unsigned displayName: Publish web server archive - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false'), ne(variables['VSCODE_ARCH'], 'universal')) - task: AzureCLI@2 inputs: @@ -367,21 +336,21 @@ steps: inputs: BuildDropPath: $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) PackageName: Visual Studio Code - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false'), ne(variables['VSCODE_ARCH'], 'universal')) + condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - publish: $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH)/_manifest displayName: Publish SBOM (client) artifact: vscode_client_darwin_$(VSCODE_ARCH)_sbom - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false'), ne(variables['VSCODE_ARCH'], 'universal')) + condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 displayName: Generate SBOM (server) inputs: BuildDropPath: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH) PackageName: Visual Studio Code Server - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false'), ne(variables['VSCODE_ARCH'], 'universal')) + condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - publish: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH)/_manifest displayName: Publish SBOM (server) artifact: vscode_server_darwin_$(VSCODE_ARCH)_sbom - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false'), ne(variables['VSCODE_ARCH'], 'universal')) + condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index 9c26e4a0fc4..285649eaae7 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -118,7 +118,7 @@ variables: - name: VSCODE_STEP_ON_IT value: ${{ eq(parameters.VSCODE_STEP_ON_IT, true) }} - name: VSCODE_BUILD_MACOS_UNIVERSAL - value: ${{ and(eq(variables['VSCODE_PUBLISH'], true), eq(parameters.VSCODE_BUILD_MACOS, true), eq(parameters.VSCODE_BUILD_MACOS_ARM64, true), eq(parameters.VSCODE_BUILD_MACOS_UNIVERSAL, true)) }} + value: ${{ and(eq(parameters.VSCODE_BUILD_MACOS, true), eq(parameters.VSCODE_BUILD_MACOS_ARM64, true), eq(parameters.VSCODE_BUILD_MACOS_UNIVERSAL, true)) }} - name: AZURE_CDN_URL value: https://az764295.vo.msecnd.net - name: AZURE_DOCUMENTDB_ENDPOINT @@ -348,7 +348,7 @@ stages: variables: VSCODE_ARCH: universal steps: - - template: darwin/product-build-darwin.yml + - template: darwin/product-build-darwin-universal.yml - ${{ if ne(variables['VSCODE_PUBLISH'], 'false') }}: - job: macOSUniversalSign dependsOn: From 42f6aedda1284e2ad5afce60556132496911712c Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 13 Apr 2022 11:38:30 -0700 Subject: [PATCH 053/245] xterm@4.19.0-beta.21 --- package.json | 6 +++--- remote/package.json | 6 +++--- remote/web/package.json | 4 ++-- remote/web/yarn.lock | 16 ++++++++-------- remote/yarn.lock | 24 ++++++++++++------------ yarn.lock | 24 ++++++++++++------------ 6 files changed, 40 insertions(+), 40 deletions(-) diff --git a/package.json b/package.json index 686ad423d90..8a50377755f 100644 --- a/package.json +++ b/package.json @@ -84,12 +84,12 @@ "vscode-proxy-agent": "^0.12.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.20", - "xterm-addon-search": "0.9.0-beta.18", + "xterm": "4.19.0-beta.21", + "xterm-addon-search": "0.9.0-beta.20", "xterm-addon-serialize": "0.7.0-beta.12", "xterm-addon-unicode11": "0.4.0-beta.3", "xterm-addon-webgl": "0.12.0-beta.27", - "xterm-headless": "4.19.0-beta.20", + "xterm-headless": "4.19.0-beta.21", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/package.json b/remote/package.json index 2173d072545..e60a3ddbdbb 100644 --- a/remote/package.json +++ b/remote/package.json @@ -24,12 +24,12 @@ "vscode-proxy-agent": "^0.12.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.20", - "xterm-addon-search": "0.9.0-beta.18", + "xterm": "4.19.0-beta.21", + "xterm-addon-search": "0.9.0-beta.20", "xterm-addon-serialize": "0.7.0-beta.12", "xterm-addon-unicode11": "0.4.0-beta.3", "xterm-addon-webgl": "0.12.0-beta.27", - "xterm-headless": "4.19.0-beta.20", + "xterm-headless": "4.19.0-beta.21", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/web/package.json b/remote/web/package.json index 63ca1649694..2ef7565d1ab 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -10,8 +10,8 @@ "tas-client-umd": "0.1.4", "vscode-oniguruma": "1.6.1", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.20", - "xterm-addon-search": "0.9.0-beta.18", + "xterm": "4.19.0-beta.21", + "xterm-addon-search": "0.9.0-beta.20", "xterm-addon-unicode11": "0.4.0-beta.3", "xterm-addon-webgl": "0.12.0-beta.27" } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 2579eb0b787..9a8f157bd65 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -113,10 +113,10 @@ vscode-textmate@7.0.1: resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-7.0.1.tgz#8118a32b02735dccd14f893b495fa5389ad7de79" integrity sha512-zQ5U/nuXAAMsh691FtV0wPz89nSkHbs+IQV8FDk+wew9BlSDhf4UmWGlWJfTR2Ti6xZv87Tj5fENzKf6Qk7aLw== -xterm-addon-search@0.9.0-beta.18: - version "0.9.0-beta.18" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.18.tgz#5317aed1dc747f468ccb7ecd151fb00d82a8a19d" - integrity sha512-SAeA3thc2WJNYXwjOEJFLpZ1ZVOs22RLmz9a6WcrzXkvCjLZRvbRGwX25Ms+Dd7dVDQNbKVUzUJohspP/vYr0Q== +xterm-addon-search@0.9.0-beta.20: + version "0.9.0-beta.20" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.20.tgz#53e0694d27bebba2b955a0ff58e1fc07670dbfb0" + integrity sha512-kKa43yZIZ6H0tgSoRTgsdLjrml1rGfB/V4fP3sFuEFnboHB0UNBEYssTVW3Mf5Uhxp0waHS/MU0zdnkyRDlC1Q== xterm-addon-unicode11@0.4.0-beta.3: version "0.4.0-beta.3" @@ -128,7 +128,7 @@ xterm-addon-webgl@0.12.0-beta.27: resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.27.tgz#afc5bc01d1ef3af9005fb9f6325a4db9c92aa8d9" integrity sha512-P948trotU8FMHtaA7C2x97VpLq6QLSjO53kWNvONS0/XwEKQBIYCI7Jfri2wcLgfQg6Cn4OQGLoj2YBK3MMyww== -xterm@4.19.0-beta.20: - version "4.19.0-beta.20" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.20.tgz#d8e970d8a8460c1d1a5ec9866f78f607a44c1349" - integrity sha512-IYI4ngSWzpV4sJXLWGEDF7vgLuUHn0CUQ42+TGv4H/hCGo4uru4s/D3Yws0ETb3a9VwRpZEPsigULaWTnhFusg== +xterm@4.19.0-beta.21: + version "4.19.0-beta.21" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.21.tgz#bcc603fdb496cae052a1c7c978c0efb531f8a8f3" + integrity sha512-WwtYP0U/NyU6Mw8JIMFHLo3+JUScOugQCqBiFdJHUoeisNEaZSwWV89mSekYLhh6iOgce7Xkq9GRPq1zPcaoYQ== diff --git a/remote/yarn.lock b/remote/yarn.lock index bd01c5e483d..9a7f3ce1a4c 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -914,10 +914,10 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -xterm-addon-search@0.9.0-beta.18: - version "0.9.0-beta.18" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.18.tgz#5317aed1dc747f468ccb7ecd151fb00d82a8a19d" - integrity sha512-SAeA3thc2WJNYXwjOEJFLpZ1ZVOs22RLmz9a6WcrzXkvCjLZRvbRGwX25Ms+Dd7dVDQNbKVUzUJohspP/vYr0Q== +xterm-addon-search@0.9.0-beta.20: + version "0.9.0-beta.20" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.20.tgz#53e0694d27bebba2b955a0ff58e1fc07670dbfb0" + integrity sha512-kKa43yZIZ6H0tgSoRTgsdLjrml1rGfB/V4fP3sFuEFnboHB0UNBEYssTVW3Mf5Uhxp0waHS/MU0zdnkyRDlC1Q== xterm-addon-serialize@0.7.0-beta.12: version "0.7.0-beta.12" @@ -934,15 +934,15 @@ xterm-addon-webgl@0.12.0-beta.27: resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.27.tgz#afc5bc01d1ef3af9005fb9f6325a4db9c92aa8d9" integrity sha512-P948trotU8FMHtaA7C2x97VpLq6QLSjO53kWNvONS0/XwEKQBIYCI7Jfri2wcLgfQg6Cn4OQGLoj2YBK3MMyww== -xterm-headless@4.19.0-beta.20: - version "4.19.0-beta.20" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.20.tgz#9e401920fcc24c2474e0bd45df932c62413594da" - integrity sha512-twp0vCyfdI4wVgDrwxaHk1FtC4UhTNNgbIPT6yVPjICOUkUTOvFjrQCNKHv2uMOJo9uAH2gyOsIqHdEP549rJA== +xterm-headless@4.19.0-beta.21: + version "4.19.0-beta.21" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.21.tgz#903c2221cb57fab0e46b929fa39e71a7ee0525d3" + integrity sha512-zgmwcb/ICIwdOIqhMirHEzhMy3qnqzUgzwl5slFuztbh2ki0GvREBm6iW8EaveqzmaDYzTVwcBa+gE+SyYuwjg== -xterm@4.19.0-beta.20: - version "4.19.0-beta.20" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.20.tgz#d8e970d8a8460c1d1a5ec9866f78f607a44c1349" - integrity sha512-IYI4ngSWzpV4sJXLWGEDF7vgLuUHn0CUQ42+TGv4H/hCGo4uru4s/D3Yws0ETb3a9VwRpZEPsigULaWTnhFusg== +xterm@4.19.0-beta.21: + version "4.19.0-beta.21" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.21.tgz#bcc603fdb496cae052a1c7c978c0efb531f8a8f3" + integrity sha512-WwtYP0U/NyU6Mw8JIMFHLo3+JUScOugQCqBiFdJHUoeisNEaZSwWV89mSekYLhh6iOgce7Xkq9GRPq1zPcaoYQ== yallist@^4.0.0: version "4.0.0" diff --git a/yarn.lock b/yarn.lock index 111eae22516..d22deb1d491 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12204,10 +12204,10 @@ xtend@~2.1.1: dependencies: object-keys "~0.4.0" -xterm-addon-search@0.9.0-beta.18: - version "0.9.0-beta.18" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.18.tgz#5317aed1dc747f468ccb7ecd151fb00d82a8a19d" - integrity sha512-SAeA3thc2WJNYXwjOEJFLpZ1ZVOs22RLmz9a6WcrzXkvCjLZRvbRGwX25Ms+Dd7dVDQNbKVUzUJohspP/vYr0Q== +xterm-addon-search@0.9.0-beta.20: + version "0.9.0-beta.20" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.20.tgz#53e0694d27bebba2b955a0ff58e1fc07670dbfb0" + integrity sha512-kKa43yZIZ6H0tgSoRTgsdLjrml1rGfB/V4fP3sFuEFnboHB0UNBEYssTVW3Mf5Uhxp0waHS/MU0zdnkyRDlC1Q== xterm-addon-serialize@0.7.0-beta.12: version "0.7.0-beta.12" @@ -12224,15 +12224,15 @@ xterm-addon-webgl@0.12.0-beta.27: resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.27.tgz#afc5bc01d1ef3af9005fb9f6325a4db9c92aa8d9" integrity sha512-P948trotU8FMHtaA7C2x97VpLq6QLSjO53kWNvONS0/XwEKQBIYCI7Jfri2wcLgfQg6Cn4OQGLoj2YBK3MMyww== -xterm-headless@4.19.0-beta.20: - version "4.19.0-beta.20" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.20.tgz#9e401920fcc24c2474e0bd45df932c62413594da" - integrity sha512-twp0vCyfdI4wVgDrwxaHk1FtC4UhTNNgbIPT6yVPjICOUkUTOvFjrQCNKHv2uMOJo9uAH2gyOsIqHdEP549rJA== +xterm-headless@4.19.0-beta.21: + version "4.19.0-beta.21" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.21.tgz#903c2221cb57fab0e46b929fa39e71a7ee0525d3" + integrity sha512-zgmwcb/ICIwdOIqhMirHEzhMy3qnqzUgzwl5slFuztbh2ki0GvREBm6iW8EaveqzmaDYzTVwcBa+gE+SyYuwjg== -xterm@4.19.0-beta.20: - version "4.19.0-beta.20" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.20.tgz#d8e970d8a8460c1d1a5ec9866f78f607a44c1349" - integrity sha512-IYI4ngSWzpV4sJXLWGEDF7vgLuUHn0CUQ42+TGv4H/hCGo4uru4s/D3Yws0ETb3a9VwRpZEPsigULaWTnhFusg== +xterm@4.19.0-beta.21: + version "4.19.0-beta.21" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.21.tgz#bcc603fdb496cae052a1c7c978c0efb531f8a8f3" + integrity sha512-WwtYP0U/NyU6Mw8JIMFHLo3+JUScOugQCqBiFdJHUoeisNEaZSwWV89mSekYLhh6iOgce7Xkq9GRPq1zPcaoYQ== y18n@^3.2.1: version "3.2.2" From 1f9a3bb7702b6a4df80ca026cb2811c62690bae3 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 13 Apr 2022 11:47:28 -0700 Subject: [PATCH 054/245] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8a50377755f..6071eeb9f33 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.67.0", - "distro": "5c715d21e609df4020231e89b9a0a684dcfa6ffd", + "distro": "142f9ce10fa53266171369134b31bd1f222b826b", "author": { "name": "Microsoft Corporation" }, From e764ae3c12787ba034fe5b3082e2d5f7ec5a6be5 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 13 Apr 2022 11:40:45 -0700 Subject: [PATCH 055/245] Check files instead of srcElement for dragging --- src/vs/workbench/contrib/webview/browser/pre/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index 8c91fdae9c9..18451a01bb8 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -1027,12 +1027,12 @@ onDomReady(() => { return; } - if (!e.dataTransfer) { + if (!e.dataTransfer || e.shiftKey) { return; } // Only handle drags from outside editor for now - if (e.target === contentWindow.document.documentElement) { + if (e.dataTransfer.items.length && Array.prototype.every.call(e.dataTransfer.items, item => item.kind === 'file')) { hostMessaging.postMessage('drag-start'); } }; From 969a399ae94f06c388b22fdc8079f948d67e41d3 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 13 Apr 2022 20:55:01 +0200 Subject: [PATCH 056/245] fix build --- build/azure-pipelines/product-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index 285649eaae7..79c2d16d79a 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -339,7 +339,7 @@ stages: steps: - template: darwin/product-build-darwin-sign.yml - - ${{ if eq(variables['VSCODE_BUILD_MACOS_UNIVERSAL'], true) }}: + - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(variables['VSCODE_BUILD_MACOS_UNIVERSAL'], true)) }}: - job: macOSUniversal dependsOn: - macOS From 977c9c9f98a6dbe65bf8da37d9e1fdead38f9e7e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 13 Apr 2022 11:59:29 -0700 Subject: [PATCH 057/245] Revert "Always use nameSpan for code lenses" This reverts commit d218b48067858f57d5e7103b0fa57c178c06bce4. Fixes #147154 Fixes #146818 TS still needs to make tweaks before we can remove this logic --- .../codeLens/baseCodeLensProvider.ts | 31 ++++++++++++++++++- .../codeLens/implementationsCodeLens.ts | 15 +++------ .../codeLens/referencesCodeLens.ts | 21 +++++-------- 3 files changed, 43 insertions(+), 24 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/codeLens/baseCodeLensProvider.ts b/extensions/typescript-language-features/src/languageFeatures/codeLens/baseCodeLensProvider.ts index 97287b9678d..890486d7902 100644 --- a/extensions/typescript-language-features/src/languageFeatures/codeLens/baseCodeLensProvider.ts +++ b/extensions/typescript-language-features/src/languageFeatures/codeLens/baseCodeLensProvider.ts @@ -8,6 +8,8 @@ import * as nls from 'vscode-nls'; import type * as Proto from '../../protocol'; import { CachedResponse } from '../../tsServer/cachedResponse'; import { ITypeScriptServiceClient } from '../../typescriptService'; +import { escapeRegExp } from '../../utils/regexp'; +import * as typeConverters from '../../utils/typeConverters'; const localize = nls.loadMessageBundle(); @@ -57,6 +59,7 @@ export abstract class TypeScriptBaseCodeLensProvider implements vscode.CodeLensP } protected abstract extractSymbol( + document: vscode.TextDocument, item: Proto.NavigationTree, parent: Proto.NavigationTree | undefined ): vscode.Range | undefined; @@ -67,7 +70,7 @@ export abstract class TypeScriptBaseCodeLensProvider implements vscode.CodeLensP parent: Proto.NavigationTree | undefined, results: vscode.Range[] ): void { - const range = this.extractSymbol(item, parent); + const range = this.extractSymbol(document, item, parent); if (range) { results.push(range); } @@ -75,3 +78,29 @@ export abstract class TypeScriptBaseCodeLensProvider implements vscode.CodeLensP item.childItems?.forEach(child => this.walkNavTree(document, child, item, results)); } } + +export function getSymbolRange( + document: vscode.TextDocument, + item: Proto.NavigationTree +): vscode.Range | undefined { + if (item.nameSpan) { + return typeConverters.Range.fromTextSpan(item.nameSpan); + } + + // In older versions, we have to calculate this manually. See #23924 + const span = item.spans && item.spans[0]; + if (!span) { + return undefined; + } + + const range = typeConverters.Range.fromTextSpan(span); + const text = document.getText(range); + + const identifierMatch = new RegExp(`^(.*?(\\b|\\W))${escapeRegExp(item.text || '')}(\\b|\\W)`, 'gm'); + const match = identifierMatch.exec(text); + const prefixLength = match ? match.index + match[1].length : 0; + const startOffset = document.offsetAt(new vscode.Position(range.start.line, range.start.character)) + prefixLength; + return new vscode.Range( + document.positionAt(startOffset), + document.positionAt(startOffset + item.text.length)); +} diff --git a/extensions/typescript-language-features/src/languageFeatures/codeLens/implementationsCodeLens.ts b/extensions/typescript-language-features/src/languageFeatures/codeLens/implementationsCodeLens.ts index 4583d6d25ec..37750021597 100644 --- a/extensions/typescript-language-features/src/languageFeatures/codeLens/implementationsCodeLens.ts +++ b/extensions/typescript-language-features/src/languageFeatures/codeLens/implementationsCodeLens.ts @@ -13,7 +13,7 @@ import { conditionalRegistration, requireGlobalConfiguration, requireSomeCapabil import { DocumentSelector } from '../../utils/documentSelector'; import { LanguageDescription } from '../../utils/languageDescription'; import * as typeConverters from '../../utils/typeConverters'; -import { ReferencesCodeLens, TypeScriptBaseCodeLensProvider } from './baseCodeLensProvider'; +import { getSymbolRange, ReferencesCodeLens, TypeScriptBaseCodeLensProvider } from './baseCodeLensProvider'; const localize = nls.loadMessageBundle(); @@ -66,26 +66,21 @@ export default class TypeScriptImplementationsCodeLensProvider extends TypeScrip } protected extractSymbol( + document: vscode.TextDocument, item: Proto.NavigationTree, _parent: Proto.NavigationTree | undefined ): vscode.Range | undefined { - if (!item.nameSpan) { - return undefined; - } - - const itemSpan = typeConverters.Range.fromTextSpan(item.nameSpan); - switch (item.kind) { case PConst.Kind.interface: - return itemSpan; + return getSymbolRange(document, item); case PConst.Kind.class: case PConst.Kind.method: case PConst.Kind.memberVariable: case PConst.Kind.memberGetAccessor: case PConst.Kind.memberSetAccessor: - if (/\babstract\b/g.test(item.kindModifiers)) { - return itemSpan; + if (item.kindModifiers.match(/\babstract\b/g)) { + return getSymbolRange(document, item); } break; } diff --git a/extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts b/extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts index 6f1cf9ca1ce..8e4f0819b7b 100644 --- a/extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts +++ b/extensions/typescript-language-features/src/languageFeatures/codeLens/referencesCodeLens.ts @@ -14,7 +14,7 @@ import { conditionalRegistration, requireGlobalConfiguration, requireSomeCapabil import { DocumentSelector } from '../../utils/documentSelector'; import { LanguageDescription } from '../../utils/languageDescription'; import * as typeConverters from '../../utils/typeConverters'; -import { ReferencesCodeLens, TypeScriptBaseCodeLensProvider } from './baseCodeLensProvider'; +import { getSymbolRange, ReferencesCodeLens, TypeScriptBaseCodeLensProvider } from './baseCodeLensProvider'; const localize = nls.loadMessageBundle(); @@ -61,24 +61,19 @@ export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLens } protected extractSymbol( + document: vscode.TextDocument, item: Proto.NavigationTree, parent: Proto.NavigationTree | undefined ): vscode.Range | undefined { - if (!item.nameSpan) { - return undefined; - } - - const itemSpan = typeConverters.Range.fromTextSpan(item.nameSpan); - if (parent && parent.kind === PConst.Kind.enum) { - return itemSpan; + return getSymbolRange(document, item); } switch (item.kind) { case PConst.Kind.function: { const showOnAllFunctions = vscode.workspace.getConfiguration(this.language.id).get('referencesCodeLens.showOnAllFunctions'); if (showOnAllFunctions) { - return itemSpan; + return getSymbolRange(document, item); } } // fallthrough @@ -88,7 +83,7 @@ export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLens case PConst.Kind.variable: // Only show references for exported variables if (/\bexport\b/.test(item.kindModifiers)) { - return itemSpan; + return getSymbolRange(document, item); } break; @@ -96,12 +91,12 @@ export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLens if (item.text === '') { break; } - return itemSpan; + return getSymbolRange(document, item); case PConst.Kind.interface: case PConst.Kind.type: case PConst.Kind.enum: - return itemSpan; + return getSymbolRange(document, item); case PConst.Kind.method: case PConst.Kind.memberGetAccessor: @@ -121,7 +116,7 @@ export class TypeScriptReferencesCodeLensProvider extends TypeScriptBaseCodeLens case PConst.Kind.class: case PConst.Kind.interface: case PConst.Kind.type: - return itemSpan; + return getSymbolRange(document, item); } break; } From bb5c5cba0b33f10c191bdddca18ce25e41c87f36 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Wed, 13 Apr 2022 12:10:39 -0700 Subject: [PATCH 058/245] get overview ruler color to upate (#147116) --- .../terminal/browser/terminalFindWidget.ts | 20 ++++++++++++++++--- .../terminal/browser/xterm/decorationAddon.ts | 10 ++++++---- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts index 6168afff93b..ffd839a6c18 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts @@ -10,6 +10,8 @@ import { FindReplaceState } from 'vs/editor/contrib/find/browser/findState'; import { ITerminalGroupService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { TerminalLocation } from 'vs/platform/terminal/common/terminal'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export class TerminalFindWidget extends SimpleFindWidget { protected _findInputFocused: IContextKey; @@ -21,7 +23,9 @@ export class TerminalFindWidget extends SimpleFindWidget { @IContextViewService _contextViewService: IContextViewService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @ITerminalService private readonly _terminalService: ITerminalService, - @ITerminalGroupService private readonly _terminalGroupService: ITerminalGroupService + @ITerminalGroupService private readonly _terminalGroupService: ITerminalGroupService, + @IThemeService private readonly _themeService: IThemeService, + @IConfigurationService private readonly _configurationService: IConfigurationService ) { super(_contextViewService, _contextKeyService, findState, { showOptionButtons: true, showResultCount: true }); @@ -31,15 +35,25 @@ export class TerminalFindWidget extends SimpleFindWidget { this._findInputFocused = TerminalContextKeys.findInputFocus.bindTo(this._contextKeyService); this._findWidgetFocused = TerminalContextKeys.findFocus.bindTo(this._contextKeyService); this._findWidgetVisible = TerminalContextKeys.findVisible.bindTo(_contextKeyService); + this._register(this._themeService.onDidColorThemeChange(() => { + if (this._findWidgetVisible) { + this.find(true, true); + } + })); + this._register(this._configurationService.onDidChangeConfiguration((e) => { + if (e.affectsConfiguration('workbench.colorCustomizations') && this._findWidgetVisible) { + this.find(true, true); + } + })); } - find(previous: boolean) { + find(previous: boolean, update?: boolean) { const instance = this._terminalService.activeInstance; if (!instance) { return; } if (previous) { - instance.xterm?.findPrevious(this.inputValue, { regex: this._getRegexValue(), wholeWord: this._getWholeWordValue(), caseSensitive: this._getCaseSensitiveValue() }); + instance.xterm?.findPrevious(this.inputValue, { regex: this._getRegexValue(), wholeWord: this._getWholeWordValue(), caseSensitive: this._getCaseSensitiveValue(), incremental: update }); } else { instance.xterm?.findNext(this.inputValue, { regex: this._getRegexValue(), wholeWord: this._getWholeWordValue(), caseSensitive: this._getCaseSensitiveValue() }); } diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts index 40b75ff1352..3f07b624fa7 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts @@ -73,6 +73,8 @@ export class DecorationAddon extends Disposable implements ITerminalAddon { this._refreshStyles(); } else if (e.affectsConfiguration(TerminalSettingId.FontSize) || e.affectsConfiguration(TerminalSettingId.LineHeight)) { this.refreshLayouts(); + } else if (e.affectsConfiguration('workbench.colorCustomizations')) { + this._refreshStyles(true); } }); this._themeService.onDidColorThemeChange(() => this._refreshStyles(true)); @@ -94,10 +96,10 @@ export class DecorationAddon extends Disposable implements ITerminalAddon { } else { color = ''; } - if (decoration.decoration.overviewRulerOptions) { - decoration.decoration.overviewRulerOptions.color = color; - } else { - decoration.decoration.overviewRulerOptions = { color }; + if (decoration.decoration.options?.overviewRulerOptions) { + decoration.decoration.options.overviewRulerOptions.color = color; + } else if (decoration.decoration.options) { + decoration.decoration.options.overviewRulerOptions = { color }; } } } From a6fc7a58de3a04f10eb11a5bc88ce8e932cd7951 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Wed, 13 Apr 2022 14:54:28 -0700 Subject: [PATCH 059/245] sort by length first and then name. --- build/azure-pipelines/product-publish.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/azure-pipelines/product-publish.ps1 b/build/azure-pipelines/product-publish.ps1 index 17b02f6aff5..5abfed48dca 100644 --- a/build/azure-pipelines/product-publish.ps1 +++ b/build/azure-pipelines/product-publish.ps1 @@ -29,8 +29,8 @@ if (Test-Path $ARTIFACT_PROCESSED_WILDCARD_PATH) { # This means that the latest artifact_processed_*.txt file has all of the contents of the previous ones. # Note: The kusto-like syntax only works in PS7+ and only in scripts, not at the REPL. Get-ChildItem $ARTIFACT_PROCESSED_WILDCARD_PATH - | Sort-Object - | Select-Object -Last 1 + # Sort by file name length first and then Name to make sure we sort numerically. Ex. 12 comes after 9. + | Sort-Object { $_.Name.Length },Name -Bottom 1 | Get-Content | ForEach-Object { $set.Add($_) | Out-Null From ab0b7d2399b3be9c30d811d5dc290f068f70cd3f Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 13 Apr 2022 15:49:25 -0700 Subject: [PATCH 060/245] Eliminate duplicate openEditor call. Fix #136829 --- .../contrib/debug/browser/callStackView.ts | 14 ++++++++------ .../contrib/debug/browser/debugCommands.ts | 2 +- .../contrib/debug/browser/debugService.ts | 6 +++--- .../contrib/debug/browser/debugSession.ts | 2 +- src/vs/workbench/contrib/debug/browser/repl.ts | 2 +- src/vs/workbench/contrib/debug/common/debug.ts | 2 +- 6 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index b23aa7e10af..6a045d3ce23 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -47,7 +47,6 @@ import { createDisconnectMenuItemAction } from 'vs/workbench/contrib/debug/brows import { CALLSTACK_VIEW_ID, CONTEXT_CALLSTACK_ITEM_STOPPED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_CALLSTACK_SESSION_HAS_ONE_THREAD, CONTEXT_CALLSTACK_SESSION_IS_ATTACH, CONTEXT_DEBUG_STATE, CONTEXT_STACK_FRAME_SUPPORTS_RESTART, getStateLabel, IDebugModel, IDebugService, IDebugSession, IRawStoppedDetails, IStackFrame, IThread, State } from 'vs/workbench/contrib/debug/common/debug'; import { StackFrame, Thread, ThreadAndSessionIds } from 'vs/workbench/contrib/debug/common/debugModel'; import { isSessionAttach } from 'vs/workbench/contrib/debug/common/debugUtils'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; const $ = dom.$; @@ -150,7 +149,6 @@ export class CallStackView extends ViewPane { @IKeybindingService keybindingService: IKeybindingService, @IInstantiationService instantiationService: IInstantiationService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService, - @IEditorService private readonly editorService: IEditorService, @IConfigurationService configurationService: IConfigurationService, @IContextKeyService contextKeyService: IContextKeyService, @IOpenerService openerService: IOpenerService, @@ -287,10 +285,10 @@ export class CallStackView extends ViewPane { return; } - const focusStackFrame = (stackFrame: IStackFrame | undefined, thread: IThread | undefined, session: IDebugSession) => { + const focusStackFrame = (stackFrame: IStackFrame | undefined, thread: IThread | undefined, session: IDebugSession, options: { explicit?: boolean; preserveFocus?: boolean; sideBySide?: boolean; pinned?: boolean } = {}) => { this.ignoreFocusStackFrameEvent = true; try { - this.debugService.focusStackFrame(stackFrame, thread, session, true); + this.debugService.focusStackFrame(stackFrame, thread, session, { ...options, ...{ explicit: true } }); } finally { this.ignoreFocusStackFrameEvent = false; } @@ -298,8 +296,12 @@ export class CallStackView extends ViewPane { const element = e.element; if (element instanceof StackFrame) { - focusStackFrame(element, element.thread, element.thread.session); - element.openInEditor(this.editorService, e.editorOptions.preserveFocus, e.sideBySide, e.editorOptions.pinned); + const opts = { + preserveFocus: e.editorOptions.preserveFocus, + sideBySide: e.sideBySide, + pinned: e.editorOptions.pinned + }; + focusStackFrame(element, element.thread, element.thread.session, opts); } if (element instanceof Thread) { focusStackFrame(undefined, element, element.session); diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index 5f6e41bf0c6..3042792f8e8 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -406,7 +406,7 @@ CommandsRegistry.registerCommand({ if (stoppedChildSession && session.state !== State.Stopped) { session = stoppedChildSession; } - await debugService.focusStackFrame(undefined, undefined, session, true); + await debugService.focusStackFrame(undefined, undefined, session, { explicit: true }); const stackFrame = debugService.getViewModel().focusedStackFrame; if (stackFrame) { await stackFrame.openInEditor(editorService, true); diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index d2665b688bd..224b72fe4fa 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -852,11 +852,11 @@ export class DebugService implements IDebugService { //---- focus management - async focusStackFrame(_stackFrame: IStackFrame | undefined, _thread?: IThread, _session?: IDebugSession, explicit?: boolean): Promise { + async focusStackFrame(_stackFrame: IStackFrame | undefined, _thread?: IThread, _session?: IDebugSession, options?: { explicit?: boolean; preserveFocus?: boolean; sideBySide?: boolean; pinned?: boolean }): Promise { const { stackFrame, thread, session } = getStackFrameThreadAndSessionToFocus(this.model, _stackFrame, _thread, _session); if (stackFrame) { - const editor = await stackFrame.openInEditor(this.editorService, true); + const editor = await stackFrame.openInEditor(this.editorService, options?.preserveFocus ?? true, options?.sideBySide, options?.pinned); if (editor) { if (editor.input === DisassemblyViewInput.instance) { // Go to address is invoked via setFocus @@ -880,7 +880,7 @@ export class DebugService implements IDebugService { this.debugType.reset(); } - this.viewModel.setFocus(stackFrame, thread, session, !!explicit); + this.viewModel.setFocus(stackFrame, thread, session, !!options?.explicit); } //---- watches diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 0bd109a80d9..0e8671611e0 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -1004,7 +1004,7 @@ export class DebugSession implements IDebugSession { this.passFocusScheduler.cancel(); if (focusedThread && event.body.threadId === focusedThread.threadId) { // De-focus the thread in case it was focused - this.debugService.focusStackFrame(undefined, undefined, viewModel.focusedSession, false); + this.debugService.focusStackFrame(undefined, undefined, viewModel.focusedSession, { explicit: false }); } } })); diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index cc2a06f698e..d171c967d4f 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -895,7 +895,7 @@ registerAction2(class extends ViewAction { session = stopppedChildSession; } } - await debugService.focusStackFrame(undefined, undefined, session, true); + await debugService.focusStackFrame(undefined, undefined, session, { explicit: true }); } // Need to select the session in the view since the focussed session might not have changed await view.selectSession(session); diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index b8e653916c5..fb0445b670e 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -951,7 +951,7 @@ export interface IDebugService { /** * Sets the focused stack frame and evaluates all expressions against the newly focused stack frame, */ - focusStackFrame(focusedStackFrame: IStackFrame | undefined, thread?: IThread, session?: IDebugSession, explicit?: boolean): Promise; + focusStackFrame(focusedStackFrame: IStackFrame | undefined, thread?: IThread, session?: IDebugSession, options?: { explicit?: boolean; preserveFocus?: boolean; sideBySide?: boolean; pinned?: boolean }): Promise; /** * Returns true if breakpoints can be set for a given editor model. Depends on mode. From ce80e8b243df25a530ea10d39970c110e935fa5f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 13 Apr 2022 14:02:15 -0700 Subject: [PATCH 061/245] Fix drag from explorer into notebook --- .../notebook/browser/view/renderers/backLayerWebView.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index d5ad9679436..f5b40f97620 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -41,6 +41,7 @@ import { INotebookKernel } from 'vs/workbench/contrib/notebook/common/notebookKe import { IScopedRendererMessaging } from 'vs/workbench/contrib/notebook/common/notebookRendererMessagingService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IWebviewElement, IWebviewService, WebviewContentPurpose } from 'vs/workbench/contrib/webview/browser/webview'; +import { WebviewWindowDragMonitor } from 'vs/workbench/contrib/webview/browser/webviewWindowDragMonitor'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { FromWebviewMessage, IAckOutputHeight, IClickedDataUrlMessage, ICodeBlockHighlightRequest, IContentWidgetTopRequest, IControllerPreload, ICreationContent, ICreationRequestMessage, IFindMatch, IMarkupCellInitialization, ToWebviewMessage } from './webviewMessages'; @@ -527,6 +528,8 @@ var requirejs = (function() { this.webview.mountTo(this.element); this._register(this.webview); + this._register(new WebviewWindowDragMonitor(() => this.webview)); + this._register(this.webview.onDidClickLink(link => { if (this._disposed) { return; From 5ec992bc086b6e6d52397b1ddc779dccda5a06ca Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 13 Apr 2022 14:32:08 -0700 Subject: [PATCH 062/245] Focus the text editor after performing a snippet edit --- src/vs/editor/contrib/snippet/browser/snippetController2.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/editor/contrib/snippet/browser/snippetController2.ts b/src/vs/editor/contrib/snippet/browser/snippetController2.ts index b1b4a6a1dd5..8d90c8e30e9 100644 --- a/src/vs/editor/contrib/snippet/browser/snippetController2.ts +++ b/src/vs/editor/contrib/snippet/browser/snippetController2.ts @@ -340,6 +340,7 @@ export function performSnippetEdit(editor: ICodeEditor, edit: SnippetTextEdit) { if (!controller) { return false; } + editor.focus(); editor.setSelection(edit.range); controller.insert(edit.snippet); return controller.isInSnippet(); From bf02dd3453a4414037e2ee1f67f994377b21a8ce Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 13 Apr 2022 16:39:22 -0700 Subject: [PATCH 063/245] Formatting --- .../contrib/webview/browser/pre/main.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index 18451a01bb8..0ac6415cf3d 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -368,16 +368,14 @@ const unloadMonitor = new class { } switch (this.confirmBeforeClose) { - case 'always': - { - event.preventDefault(); - event.returnValue = ''; - return ''; - } - case 'never': - { - break; - } + case 'always': { + event.preventDefault(); + event.returnValue = ''; + return ''; + } + case 'never': { + break; + } case 'keyboardOnly': default: { if (this.isModifierKeyDown) { From c485c00298d3341d16539554ee66f6dace15811f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 13 Apr 2022 16:49:55 -0700 Subject: [PATCH 064/245] Start forwarding events from webview even before contents have loaded Fixes #144797 --- .../contrib/webview/browser/pre/main.js | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/webview/browser/pre/main.js b/src/vs/workbench/contrib/webview/browser/pre/main.js index 0ac6415cf3d..d2c57f4aa10 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/main.js +++ b/src/vs/workbench/contrib/webview/browser/pre/main.js @@ -678,6 +678,22 @@ const handleInnerScroll = (event) => { }); }; +function handleInnerDragStartEvent(/** @type {DragEvent} */ e) { + if (e.defaultPrevented) { + // Extension code has already handled this event + return; + } + + if (!e.dataTransfer || e.shiftKey) { + return; + } + + // Only handle drags from outside editor for now + if (e.dataTransfer.items.length && Array.prototype.every.call(e.dataTransfer.items, item => item.kind === 'file')) { + hostMessaging.postMessage('drag-start'); + } +} + /** * @param {() => void} callback */ @@ -1019,23 +1035,8 @@ onDomReady(() => { }); }); - const dragHandler = (/** @type {DragEvent} */ e) => { - if (e.defaultPrevented) { - // Extension code has already handled this event - return; - } - - if (!e.dataTransfer || e.shiftKey) { - return; - } - - // Only handle drags from outside editor for now - if (e.dataTransfer.items.length && Array.prototype.every.call(e.dataTransfer.items, item => item.kind === 'file')) { - hostMessaging.postMessage('drag-start'); - } - }; - contentWindow.addEventListener('dragenter', dragHandler); - contentWindow.addEventListener('dragover', dragHandler); + contentWindow.addEventListener('dragenter', handleInnerDragStartEvent); + contentWindow.addEventListener('dragover', handleInnerDragStartEvent); unloadMonitor.onIframeLoaded(newFrame); } @@ -1123,5 +1124,10 @@ onDomReady(() => { } }; + // Also forward events before the contents of the webview have loaded + window.addEventListener('keydown', handleInnerKeydown); + window.addEventListener('dragenter', handleInnerDragStartEvent); + window.addEventListener('dragover', handleInnerDragStartEvent); + hostMessaging.signalReady(); }); From b384d17abb626d4b8300697f08b12055a97fb5fa Mon Sep 17 00:00:00 2001 From: Aman Khalid Date: Wed, 13 Apr 2022 20:10:19 -0400 Subject: [PATCH 065/245] Fix #146166: Snippet transform preserves existing camel/Pascal case --- src/vs/editor/contrib/snippet/browser/snippetParser.ts | 9 +++------ .../contrib/snippet/test/browser/snippetParser.test.ts | 6 ++++++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/contrib/snippet/browser/snippetParser.ts b/src/vs/editor/contrib/snippet/browser/snippetParser.ts index b896cff1f74..01baba9eeff 100644 --- a/src/vs/editor/contrib/snippet/browser/snippetParser.ts +++ b/src/vs/editor/contrib/snippet/browser/snippetParser.ts @@ -395,8 +395,7 @@ export class FormatString extends Marker { return value; } return match.map(word => { - return word.charAt(0).toUpperCase() - + word.substr(1).toLowerCase(); + return word.charAt(0).toUpperCase() + word.substr(1); }) .join(''); } @@ -408,11 +407,9 @@ export class FormatString extends Marker { } return match.map((word, index) => { if (index === 0) { - return word.toLowerCase(); - } else { - return word.charAt(0).toUpperCase() - + word.substr(1).toLowerCase(); + return word.charAt(0).toLowerCase() + word.substr(1); } + return word.charAt(0).toUpperCase() + word.substr(1); }) .join(''); } 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 26cef12d7b3..320aa6af88e 100644 --- a/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts +++ b/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts @@ -656,8 +656,14 @@ suite('SnippetParser', () => { assert.strictEqual(new FormatString(1, 'capitalize').resolve('bar no repeat'), 'Bar no repeat'); assert.strictEqual(new FormatString(1, 'pascalcase').resolve('bar-foo'), 'BarFoo'); assert.strictEqual(new FormatString(1, 'pascalcase').resolve('bar-42-foo'), 'Bar42Foo'); + assert.strictEqual(new FormatString(1, 'pascalcase').resolve('snake_AndPascalCase'), 'SnakeAndPascalCase'); + assert.strictEqual(new FormatString(1, 'pascalcase').resolve('kebab-AndPascalCase'), 'KebabAndPascalCase'); + assert.strictEqual(new FormatString(1, 'pascalcase').resolve('_justPascalCase'), 'JustPascalCase'); assert.strictEqual(new FormatString(1, 'camelcase').resolve('bar-foo'), 'barFoo'); assert.strictEqual(new FormatString(1, 'camelcase').resolve('bar-42-foo'), 'bar42Foo'); + assert.strictEqual(new FormatString(1, 'camelcase').resolve('snake_AndCamelCase'), 'snakeAndCamelCase'); + assert.strictEqual(new FormatString(1, 'camelcase').resolve('kebab-AndCamelCase'), 'kebabAndCamelCase'); + assert.strictEqual(new FormatString(1, 'camelcase').resolve('_JustCamelCase'), 'justCamelCase'); assert.strictEqual(new FormatString(1, 'notKnown').resolve('input'), 'input'); // if From f64927b52f894cdb1c0643f2dcfe8c75b1de03f7 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 13 Apr 2022 17:18:39 -0700 Subject: [PATCH 066/245] Use a real button for lazy variables. Fix #143602 --- .../contrib/debug/browser/baseDebugView.ts | 3 ++- .../workbench/contrib/debug/browser/debugColors.ts | 7 ++++++- .../debug/browser/media/debug.contribution.css | 13 +++++++------ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts index acdf0b9005d..89f39336b31 100644 --- a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts +++ b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts @@ -9,6 +9,7 @@ import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { HighlightedLabel, IHighlight } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; import { IInputValidationOptions, InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; import { ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; +import { Codicon } from 'vs/base/common/codicons'; import { createMatches, FuzzyScore } from 'vs/base/common/filters'; import { once } from 'vs/base/common/functional'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -158,7 +159,7 @@ export abstract class AbstractExpressionsRenderer implements ITreeRenderer Date: Wed, 13 Apr 2022 18:14:17 -0700 Subject: [PATCH 067/245] add context menu to layout control --- src/vs/base/browser/ui/toolbar/toolbar.ts | 2 ++ .../browser/parts/titlebar/titlebarPart.ts | 33 +++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index 02c98734105..d5811906eda 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -28,6 +28,7 @@ export interface IToolBarOptions { anchorAlignmentProvider?: () => AnchorAlignment; renderDropdownAsChildElement?: boolean; moreIcon?: CSSIcon; + allowContextMenu?: boolean; } /** @@ -63,6 +64,7 @@ export class ToolBar extends Disposable { orientation: options.orientation, ariaLabel: options.ariaLabel, actionRunner: options.actionRunner, + allowContextMenu: options.allowContextMenu, actionViewItemProvider: (action: IAction) => { if (action.id === ToggleMenuAction.ID) { this.toggleMenuActionViewItem = new DropdownMenuActionViewItem( diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 4934df5077c..b22c4f246ae 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -12,7 +12,7 @@ import { getZoomFactor } from 'vs/base/browser/browser'; import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility } from 'vs/platform/window/common/window'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { IAction } from 'vs/base/common/actions'; +import { IAction, toAction } from 'vs/base/common/actions'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { DisposableStore, dispose } from 'vs/base/common/lifecycle'; @@ -411,9 +411,16 @@ export class TitlebarPart extends Part implements ITitleService { this.layoutToolbar = new ToolBar(layoutDropdownContainer, this.contextMenuService, { actionViewItemProvider: action => { return createActionViewItem(this.instantiationService, action); - } + }, + allowContextMenu: true }); + this._register(addDisposableListener(layoutDropdownContainer, EventType.CONTEXT_MENU, e => { + EventHelper.stop(e); + + this.onLayoutControlContextMenu(e); + })); + const menu = this._register(this.menuService.createMenu(MenuId.LayoutControlMenu, this.contextKeyService)); const updateLayoutMenu = () => { @@ -507,7 +514,6 @@ export class TitlebarPart extends Part implements ITitleService { } private onContextMenu(e: MouseEvent): void { - // Find target anchor const event = new StandardMouseEvent(e); const anchor = { x: event.posx, y: event.posy }; @@ -524,6 +530,27 @@ export class TitlebarPart extends Part implements ITitleService { }); } + private onLayoutControlContextMenu(e: MouseEvent): void { + // Find target anchor + const event = new StandardMouseEvent(e); + const anchor = { x: event.posx, y: event.posy }; + + const actions: IAction[] = []; + actions.push(toAction({ + id: 'layoutControl.hide', + label: localize('layoutControl.hide', "Hide Layout Control"), + run: () => { + this.configurationService.updateValue('workbench.layoutControl.enabled', false); + } + })); + + // Show it + this.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => actions, + }); + } + protected adjustTitleMarginToCenter(): void { if (this.customMenubar && this.menubar) { const leftMarker = (this.appIcon ? this.appIcon.clientWidth : 0) + this.menubar.clientWidth + 10; From 55759234d736b6e7d2c50bebab0e7283fd01f691 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 14 Apr 2022 08:14:40 +0200 Subject: [PATCH 068/245] preload - revert retry logic which does not seem to help --- .../parts/sandbox/electron-browser/preload.js | 58 ++----------------- test/automation/src/application.ts | 6 +- .../src/areas/extensions/extensions.test.ts | 2 + .../src/areas/workbench/localization.test.ts | 2 + 4 files changed, 11 insertions(+), 57 deletions(-) diff --git a/src/vs/base/parts/sandbox/electron-browser/preload.js b/src/vs/base/parts/sandbox/electron-browser/preload.js index 3b74b9236e1..d8cc4c63220 100644 --- a/src/vs/base/parts/sandbox/electron-browser/preload.js +++ b/src/vs/base/parts/sandbox/electron-browser/preload.js @@ -9,10 +9,6 @@ const { ipcRenderer, webFrame, contextBridge } = require('electron'); - /** - * @typedef {import('../common/sandboxTypes').ISandboxConfiguration} ISandboxConfiguration - */ - //#region Utilities /** @@ -53,51 +49,14 @@ return undefined; } - /** - * @param {string} channel - * @param {number} retryDelay - * @returns {Promise} - */ - async function invokeWithRetry(channel, retryDelay) { - let timeoutHandle; - - // A timeout promise that resolves after `retryDelay` - const timeout = new Promise(resolve => { - timeoutHandle = setTimeout(() => { - resolve(); - }, retryDelay); - }); - - // A first `invoke` call that clears the timeout - const firstInvoke = ((async () => { - try { - return await ipcRenderer.invoke(channel); - } finally { - clearTimeout(timeoutHandle); - } - })()); - - // Race the `invoke` to the `setTimeout` - const result = await Promise.race([ - firstInvoke, - timeout - ]); - - // If we have a result, return immediately - if (result) { - return result; - } - - console.warn(`[preload] ipcRenderer.invoke(${channel}) did not return after ${retryDelay}ms. Retrying once...`); - - // Otherwise, we retry once on the same channel - return ipcRenderer.invoke(channel); - } - //#endregion //#region Resolve Configuration + /** + * @typedef {import('../common/sandboxTypes').ISandboxConfiguration} ISandboxConfiguration + */ + /** @type {ISandboxConfiguration | undefined} */ let configuration = undefined; @@ -112,14 +71,7 @@ if (validateIPC(windowConfigIpcChannel)) { // Resolve configuration from electron-main - // - // TODO@electron there seems to be a condition where an early - // `ipcRenderer.invoke` call does not return when running in - // smoke tests where a debugger is attached. The workaround - // here is to retry the call, but the underlying reasons are - // not yet understood. - // (https://github.com/microsoft/vscode/issues/146785) - configuration = await invokeWithRetry(windowConfigIpcChannel, 5000); + configuration = await ipcRenderer.invoke(windowConfigIpcChannel); // Apply `userEnv` directly Object.assign(process.env, configuration.userEnv); diff --git a/test/automation/src/application.ts b/test/automation/src/application.ts index 8914ed55982..4506ed8e14e 100644 --- a/test/automation/src/application.ts +++ b/test/automation/src/application.ts @@ -6,7 +6,6 @@ import { Workbench } from './workbench'; import { Code, launch, LaunchOptions } from './code'; import { Logger, measureAndLog } from './logger'; -import { PlaywrightDriver } from './playwrightDriver'; export const enum Quality { Dev, @@ -135,10 +134,9 @@ export class Application { } private async checkWorkbenchReady(code: Code): Promise { - const driver = code.driver; // Web / Legacy: just poll for workbench element - if (this.web || !(driver instanceof PlaywrightDriver)) { + if (this.web) { await measureAndLog(code.waitForElement('.monaco-workbench'), 'Application#checkWindowReady: wait for .monaco-workbench element', this.logger); } @@ -151,7 +149,7 @@ export class Application { } catch (error) { this.logger.log(`checkWindowReady: giving up after 10s, reloading window and trying again...`); - await driver.reload(); + await code.driver.reload(); return this.checkWorkbenchReady(code); } diff --git a/test/smoke/src/areas/extensions/extensions.test.ts b/test/smoke/src/areas/extensions/extensions.test.ts index 7a4875bcd7f..9bf6dd79b07 100644 --- a/test/smoke/src/areas/extensions/extensions.test.ts +++ b/test/smoke/src/areas/extensions/extensions.test.ts @@ -13,6 +13,8 @@ export function setup(logger: Logger) { installAllHandlers(logger); it('install and enable vscode-smoketest-check extension', async function () { + this.retries(3); // https://github.com/microsoft/vscode/issues/146800 + const app = this.app as Application; await app.workbench.extensions.openExtensionsViewlet(); diff --git a/test/smoke/src/areas/workbench/localization.test.ts b/test/smoke/src/areas/workbench/localization.test.ts index afa4e9fced4..61b354a22f3 100644 --- a/test/smoke/src/areas/workbench/localization.test.ts +++ b/test/smoke/src/areas/workbench/localization.test.ts @@ -14,6 +14,8 @@ export function setup(logger: Logger) { installAllHandlers(logger); it('starts with "DE" locale and verifies title and viewlets text is in German', async function () { + this.retries(3); // https://github.com/microsoft/vscode/issues/146800 + const app = this.app as Application; await app.workbench.extensions.openExtensionsViewlet(); await app.workbench.extensions.installExtension('ms-ceintl.vscode-language-pack-de', false); From e34318c3590ef25e4a095d141b8a138dfd048e14 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 14 Apr 2022 08:18:51 +0200 Subject: [PATCH 069/245] Web API change (#147392) * for #147368 * preserve API --- src/vs/workbench/browser/web.api.ts | 2 +- src/vs/workbench/browser/web.factory.ts | 2 +- src/vs/workbench/browser/web.main.ts | 8 +++++--- src/vs/workbench/workbench.web.main.ts | 1 - 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/browser/web.api.ts b/src/vs/workbench/browser/web.api.ts index 35af583e718..9b365d6a9c5 100644 --- a/src/vs/workbench/browser/web.api.ts +++ b/src/vs/workbench/browser/web.api.ts @@ -44,7 +44,7 @@ export interface IWorkbench { * @returns the scheme to use for opening the associated desktop * experience via protocol handler. */ - readonly uriScheme: string; + getUriScheme(): Promise; /** * Retrieve performance marks that have been collected during startup. This function diff --git a/src/vs/workbench/browser/web.factory.ts b/src/vs/workbench/browser/web.factory.ts index cc1945ea86c..49c7eb489e9 100644 --- a/src/vs/workbench/browser/web.factory.ts +++ b/src/vs/workbench/browser/web.factory.ts @@ -111,7 +111,7 @@ export namespace env { export async function getUriScheme(): Promise { const workbench = await workbenchPromise.p; - return workbench.env.uriScheme; + return workbench.env.getUriScheme(); } /** diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 8e486898b4f..5ad8d30aacc 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -126,7 +126,10 @@ export class BrowserMain extends Disposable { executeCommand: (command, ...args) => commandService.executeCommand(command, ...args) }, env: { - uriScheme: productService.urlProtocol, + telemetryLevel: telemetryService.telemetryLevel, + async getUriScheme(): Promise { + return productService.urlProtocol; + }, async retrievePerformanceMarks() { await timerService.whenReady(); @@ -134,8 +137,7 @@ export class BrowserMain extends Disposable { }, async openUri(uri: URI): Promise { return openerService.open(uri, {}); - }, - telemetryLevel: telemetryService.telemetryLevel, + } }, window: { withProgress: (options, task) => progessService.withProgress(options, task) diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index 4376a8c5e27..c58c29708e7 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -181,7 +181,6 @@ import type { IUpdateProvider, IUpdate } from 'vs/workbench/services/update/brow // eslint-disable-next-line no-duplicate-imports import type { IWorkspace, IWorkspaceProvider } from 'vs/workbench/services/host/browser/browserHostService'; - export { // Factory From c1eefb017056c6747c6454e020259d9f6d8f0815 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 14 Apr 2022 09:09:07 +0200 Subject: [PATCH 070/245] add macOSTest job Co-Authored-By: @TylerLeonhardt --- .../darwin/product-build-darwin-test.yml | 274 ++++++++++++++++++ .../darwin/product-build-darwin.yml | 135 --------- build/azure-pipelines/product-build.yml | 16 +- 3 files changed, 285 insertions(+), 140 deletions(-) create mode 100644 build/azure-pipelines/darwin/product-build-darwin-test.yml diff --git a/build/azure-pipelines/darwin/product-build-darwin-test.yml b/build/azure-pipelines/darwin/product-build-darwin-test.yml new file mode 100644 index 00000000000..29e628df217 --- /dev/null +++ b/build/azure-pipelines/darwin/product-build-darwin-test.yml @@ -0,0 +1,274 @@ +steps: + - task: NodeTool@0 + inputs: + versionSpec: "16.x" + + - task: AzureKeyVault@1 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: "vscode-builds-subscription" + KeyVaultName: vscode + SecretsFilter: "github-distro-mixin-password,macos-developer-certificate,macos-developer-certificate-key" + + - task: DownloadPipelineArtifact@2 + inputs: + artifact: Compilation + path: $(Build.ArtifactStagingDirectory) + displayName: Download compilation output + + - script: | + set -e + tar -xzf $(Build.ArtifactStagingDirectory)/compilation.tar.gz + displayName: Extract compilation output + + # Set up the credentials to retrieve distro repo and setup git persona + # to create a merge commit for when we merge distro into oss + - script: | + set -e + cat << EOF > ~/.netrc + machine github.com + login vscode + password $(github-distro-mixin-password) + EOF + + git config user.email "vscode@microsoft.com" + git config user.name "VSCode" + displayName: Prepare tooling + + - script: | + set -e + git fetch https://github.com/$(VSCODE_MIXIN_REPO).git $VSCODE_DISTRO_REF + echo "##vso[task.setvariable variable=VSCODE_DISTRO_COMMIT;]$(git rev-parse FETCH_HEAD)" + git checkout FETCH_HEAD + condition: and(succeeded(), ne(variables.VSCODE_DISTRO_REF, ' ')) + displayName: Checkout override commit + + - script: | + set -e + git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") + displayName: Merge distro + + - script: | + mkdir -p .build + node build/azure-pipelines/common/computeNodeModulesCacheKey.js $VSCODE_ARCH $ENABLE_TERRAPIN > .build/yarnlockhash + displayName: Prepare yarn cache flags + + - task: Cache@2 + inputs: + key: "nodeModules | $(Agent.OS) | .build/yarnlockhash" + path: .build/node_modules_cache + cacheHitVar: NODE_MODULES_RESTORED + displayName: Restore node_modules cache + + - script: | + set -e + tar -xzf .build/node_modules_cache/cache.tgz + condition: and(succeeded(), eq(variables.NODE_MODULES_RESTORED, 'true')) + displayName: Extract node_modules cache + + - script: | + set -e + npm install -g node-gyp@latest + node-gyp --version + displayName: Update node-gyp + condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + + - script: | + set -e + npx https://aka.ms/enablesecurefeed standAlone + timeoutInMinutes: 5 + retryCountOnTaskFailure: 3 + condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) + displayName: Switch to Terrapin packages + + - script: | + set -e + export npm_config_arch=$(VSCODE_ARCH) + export npm_config_node_gyp=$(which node-gyp) + + for i in {1..3}; do # try 3 times, for Terrapin + yarn --frozen-lockfile --check-files && break + if [ $i -eq 3 ]; then + echo "Yarn failed too many times" >&2 + exit 1 + fi + echo "Yarn failed $i, trying again..." + done + env: + ELECTRON_SKIP_BINARY_DOWNLOAD: 1 + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 + GITHUB_TOKEN: "$(github-distro-mixin-password)" + displayName: Install dependencies + condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + + - script: | + set -e + node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt + mkdir -p .build/node_modules_cache + tar -czf .build/node_modules_cache/cache.tgz --files-from .build/node_modules_list.txt + condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + displayName: Create node_modules archive + + # This script brings in the right resources (images, icons, etc) based on the quality (insiders, stable, exploration) + - script: | + set -e + node build/azure-pipelines/mixin + displayName: Mix in quality + + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-darwin-$(VSCODE_ARCH)-min-ci + displayName: Build client + + - script: | + set -e + node build/azure-pipelines/mixin --server + displayName: Mix in server quality + + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-reh-darwin-$(VSCODE_ARCH)-min-ci + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn gulp vscode-reh-web-darwin-$(VSCODE_ARCH)-min-ci + displayName: Build Server + + - script: | + set -e + VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ + yarn npm-run-all -lp "electron $(VSCODE_ARCH)" "playwright-install" + displayName: Download Electron and Playwright + condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + + # Setting hardened entitlements is a requirement for: + # * Running tests on Big Sur (because Big Sur has additional security precautions) + - script: | + set -e + security create-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain + security default-keychain -s $(agent.tempdirectory)/buildagent.keychain + security unlock-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain + echo "$(macos-developer-certificate)" | base64 -D > $(agent.tempdirectory)/cert.p12 + security import $(agent.tempdirectory)/cert.p12 -k $(agent.tempdirectory)/buildagent.keychain -P "$(macos-developer-certificate-key)" -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k pwd $(agent.tempdirectory)/buildagent.keychain + VSCODE_ARCH=$(VSCODE_ARCH) DEBUG=electron-osx-sign* node build/darwin/sign.js + displayName: Set Hardened Entitlements + + - script: | + set -e + ./scripts/test.sh --build --tfs "Unit Tests" + displayName: Run unit tests (Electron) + timeoutInMinutes: 15 + + - script: | + set -e + yarn test-node --build + displayName: Run unit tests (node.js) + timeoutInMinutes: 15 + + - script: | + set -e + DEBUG=*browser* yarn test-browser-no-install --sequential --build --browser chromium --browser webkit --tfs "Browser Unit Tests" + displayName: Run unit tests (Browser, Chromium & Webkit) + timeoutInMinutes: 30 + + - script: | + # Figure out the full absolute path of the product we just built + # including the remote server and configure the integration tests + # to run with these builds instead of running out of sources. + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) + APP_NAME="`ls $APP_ROOT | head -n 1`" + INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH)" \ + ./scripts/test-integration.sh --build --tfs "Integration Tests" + displayName: Run integration tests (Electron) + timeoutInMinutes: 20 + + - script: | + set -e + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin-$(VSCODE_ARCH)" \ + ./scripts/test-web-integration.sh --browser webkit + displayName: Run integration tests (Browser, Webkit) + timeoutInMinutes: 20 + + - script: | + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) + APP_NAME="`ls $APP_ROOT | head -n 1`" + INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH)" \ + ./scripts/test-remote-integration.sh + displayName: Run integration tests (Remote) + timeoutInMinutes: 20 + + - script: | + set -e + ps -ef + displayName: Diagnostics before smoke test run + continueOnError: true + condition: succeededOrFailed() + + - script: | + set -e + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin-$(VSCODE_ARCH)" \ + yarn smoketest-no-compile --web --tracing --headless + timeoutInMinutes: 10 + displayName: Run smoke tests (Browser, Chromium) + + - script: | + set -e + 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 + displayName: Run smoke tests (Electron) + + - script: | + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) + 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 + displayName: Run smoke tests (Remote) + + - script: | + set -e + ps -ef + displayName: Diagnostics after smoke test run + continueOnError: true + condition: succeededOrFailed() + + - task: PublishPipelineArtifact@0 + inputs: + artifactName: crash-dump-macos-$(VSCODE_ARCH) + targetPath: .build/crashes + displayName: "Publish Crash Reports" + continueOnError: true + condition: failed() + + # In order to properly symbolify above crash reports + # (if any), we need the compiled native modules too + - task: PublishPipelineArtifact@0 + inputs: + artifactName: node-modules-macos-$(VSCODE_ARCH) + targetPath: node_modules + displayName: "Publish Node Modules" + continueOnError: true + condition: failed() + + - task: PublishPipelineArtifact@0 + inputs: + artifactName: logs-macos-$(VSCODE_ARCH)-$(System.JobAttempt) + targetPath: .build/logs + displayName: "Publish Log Files" + continueOnError: true + condition: succeededOrFailed() + + - task: PublishTestResults@2 + displayName: Publish Tests Results + inputs: + testResultsFiles: "*-results.xml" + searchFolder: "$(Build.ArtifactStagingDirectory)/test-results" + condition: succeededOrFailed() diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index c94a51b3f73..5c524dd42fb 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -132,13 +132,6 @@ steps: yarn gulp vscode-reh-web-darwin-$(VSCODE_ARCH)-min-ci displayName: Build Server - - script: | - set -e - VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ - yarn npm-run-all -lp "electron $(VSCODE_ARCH)" "playwright-install" - displayName: Download Electron and Playwright - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - # Setting hardened entitlements is a requirement for: # * Apple notarization # * Running tests on Big Sur (because Big Sur has additional security precautions) @@ -153,134 +146,6 @@ steps: VSCODE_ARCH=$(VSCODE_ARCH) DEBUG=electron-osx-sign* node build/darwin/sign.js displayName: Set Hardened Entitlements - - script: | - set -e - ./scripts/test.sh --build --tfs "Unit Tests" - displayName: Run unit tests (Electron) - timeoutInMinutes: 15 - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - - script: | - set -e - yarn test-node --build - displayName: Run unit tests (node.js) - timeoutInMinutes: 15 - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - - script: | - set -e - DEBUG=*browser* yarn test-browser-no-install --sequential --build --browser chromium --browser webkit --tfs "Browser Unit Tests" - displayName: Run unit tests (Browser, Chromium & Webkit) - timeoutInMinutes: 30 - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - - script: | - # Figure out the full absolute path of the product we just built - # including the remote server and configure the integration tests - # to run with these builds instead of running out of sources. - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) - APP_NAME="`ls $APP_ROOT | head -n 1`" - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH)" \ - ./scripts/test-integration.sh --build --tfs "Integration Tests" - displayName: Run integration tests (Electron) - timeoutInMinutes: 20 - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - - script: | - set -e - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin-$(VSCODE_ARCH)" \ - ./scripts/test-web-integration.sh --browser webkit - displayName: Run integration tests (Browser, Webkit) - timeoutInMinutes: 20 - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - - script: | - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) - APP_NAME="`ls $APP_ROOT | head -n 1`" - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin-$(VSCODE_ARCH)" \ - ./scripts/test-remote-integration.sh - displayName: Run integration tests (Remote) - timeoutInMinutes: 20 - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - - script: | - set -e - ps -ef - displayName: Diagnostics before smoke test run - continueOnError: true - condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - - script: | - set -e - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin-$(VSCODE_ARCH)" \ - yarn smoketest-no-compile --web --tracing --headless - timeoutInMinutes: 10 - displayName: Run smoke tests (Browser, Chromium) - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - - script: | - set -e - 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 - displayName: Run smoke tests (Electron) - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - - script: | - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) - 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 - displayName: Run smoke tests (Remote) - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - - script: | - set -e - ps -ef - displayName: Diagnostics after smoke test run - continueOnError: true - condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - - task: PublishPipelineArtifact@0 - inputs: - artifactName: crash-dump-macos-$(VSCODE_ARCH) - targetPath: .build/crashes - displayName: "Publish Crash Reports" - continueOnError: true - condition: failed() - - # In order to properly symbolify above crash reports - # (if any), we need the compiled native modules too - - task: PublishPipelineArtifact@0 - inputs: - artifactName: node-modules-macos-$(VSCODE_ARCH) - targetPath: node_modules - displayName: "Publish Node Modules" - continueOnError: true - condition: failed() - - - task: PublishPipelineArtifact@0 - inputs: - artifactName: logs-macos-$(VSCODE_ARCH)-$(System.JobAttempt) - targetPath: .build/logs - displayName: "Publish Log Files" - continueOnError: true - condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - - task: PublishTestResults@2 - displayName: Publish Tests Results - inputs: - testResultsFiles: "*-results.xml" - searchFolder: "$(Build.ArtifactStagingDirectory)/test-results" - condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - - script: | set -e pushd $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) && zip -r -X -y $(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH).zip * && popd diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index 79c2d16d79a..ff13d02ed8a 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -306,13 +306,19 @@ stages: BUILDSECMON_OPT_IN: true jobs: - ${{ if eq(parameters.VSCODE_BUILD_MACOS, true) }}: - - job: macOS + - job: macOSTest timeoutInMinutes: 90 variables: VSCODE_ARCH: x64 steps: - - template: darwin/product-build-darwin.yml - - ${{ if ne(variables['VSCODE_PUBLISH'], 'false') }}: + - template: darwin/product-build-darwin-test.yml + - ${{ if eq(variables['VSCODE_CIBUILD'], false) }}: + - job: macOS + timeoutInMinutes: 90 + variables: + VSCODE_ARCH: x64 + steps: + - template: darwin/product-build-darwin.yml - job: macOSSign dependsOn: - macOS @@ -329,7 +335,7 @@ stages: VSCODE_ARCH: arm64 steps: - template: darwin/product-build-darwin.yml - - ${{ if ne(variables['VSCODE_PUBLISH'], 'false') }}: + - ${{ if eq(variables['VSCODE_CIBUILD'], false) }}: - job: macOSARM64Sign dependsOn: - macOSARM64 @@ -349,7 +355,7 @@ stages: VSCODE_ARCH: universal steps: - template: darwin/product-build-darwin-universal.yml - - ${{ if ne(variables['VSCODE_PUBLISH'], 'false') }}: + - ${{ if eq(variables['VSCODE_CIBUILD'], false) }}: - job: macOSUniversalSign dependsOn: - macOSUniversal From 17b1eae984a1b3dd7dacaeba19641b1d9afd79e5 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 14 Apr 2022 09:24:44 +0200 Subject: [PATCH 071/245] docs --- src/vs/base/parts/ipc/electron-main/ipcMain.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/parts/ipc/electron-main/ipcMain.ts b/src/vs/base/parts/ipc/electron-main/ipcMain.ts index 2d95a9f2f50..6dd54fb22e3 100644 --- a/src/vs/base/parts/ipc/electron-main/ipcMain.ts +++ b/src/vs/base/parts/ipc/electron-main/ipcMain.ts @@ -115,7 +115,7 @@ class ValidatedIpcMain implements Event.NodeEventEmitter { const url = sender.url; if (!url) { - return true; // seems to happen sometimes from the early `invoke` call from preload script + return true; // TODO@electron this only seems to happen from playwright runs (https://github.com/microsoft/vscode/issues/147301) } let host = 'unknown'; From 69225f5efec44cd8e26761fca14bd72e9ef6c610 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 14 Apr 2022 12:58:58 +0530 Subject: [PATCH 072/245] inline extensions scanner --- .../node/extensionManagementService.ts | 277 ++++++++++++++++- .../node/extensionsScanner.ts | 292 ------------------ 2 files changed, 273 insertions(+), 296 deletions(-) delete mode 100644 src/vs/platform/extensionManagement/node/extensionsScanner.ts diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 81f5764cb47..2e52feaa54f 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -3,19 +3,22 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Promises, Queue } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { IStringDictionary } from 'vs/base/common/collections'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { getErrorMessage } from 'vs/base/common/errors'; +import { Disposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import * as path from 'vs/base/common/path'; -import { isMacintosh } from 'vs/base/common/platform'; +import { isMacintosh, isWindows } from 'vs/base/common/platform'; import { joinPath } from 'vs/base/common/resources'; import * as semver from 'vs/base/common/semver/semver'; import { isBoolean, isUndefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import * as pfs from 'vs/base/node/pfs'; -import { IFile, zip } from 'vs/base/node/zip'; +import { extract, ExtractError, IFile, zip } from 'vs/base/node/zip'; import * as nls from 'vs/nls'; import { IDownloadService } from 'vs/platform/download/common/download'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -24,12 +27,12 @@ import { ExtensionManagementError, ExtensionManagementErrorCode, IExtensionGalleryService, IExtensionIdentifier, IExtensionManagementService, IGalleryExtension, IGalleryMetadata, ILocalExtension, InstallOperation, InstallOptions, InstallVSIXOptions, Metadata } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { areSameExtensions, computeTargetPlatform, ExtensionKey, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { areSameExtensions, computeTargetPlatform, ExtensionKey, getGalleryExtensionId, groupByExtension } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { IExtensionsScannerService, IScannedExtension, ScanOptions } from 'vs/platform/extensionManagement/common/extensionsScannerService'; import { ExtensionsDownloader } from 'vs/platform/extensionManagement/node/extensionDownloader'; import { ExtensionsLifecycle } from 'vs/platform/extensionManagement/node/extensionLifecycle'; import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; import { ExtensionsManifestCache } from 'vs/platform/extensionManagement/node/extensionsManifestCache'; -import { ExtensionsScanner } from 'vs/platform/extensionManagement/node/extensionsScanner'; import { ExtensionsWatcher } from 'vs/platform/extensionManagement/node/extensionsWatcher'; import { ExtensionType, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions'; import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator'; @@ -187,6 +190,272 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi } +class ExtensionsScanner extends Disposable { + + private readonly uninstalledPath: string; + private readonly uninstalledFileLimiter: Queue; + + constructor( + private readonly beforeRemovingExtension: (e: ILocalExtension) => Promise, + @IFileService private readonly fileService: IFileService, + @IExtensionsScannerService private readonly extensionsScannerService: IExtensionsScannerService, + @ILogService private readonly logService: ILogService, + ) { + super(); + this.uninstalledPath = joinPath(this.extensionsScannerService.userExtensionsLocation, '.obsolete').fsPath; + this.uninstalledFileLimiter = new Queue(); + } + + async cleanUp(): Promise { + await this.removeUninstalledExtensions(); + await this.removeOutdatedExtensions(); + } + + async scanExtensions(type: ExtensionType | null): Promise { + const scannedOptions: ScanOptions = { includeInvalid: true }; + let scannedExtensions: IScannedExtension[] = []; + if (type === null || type === ExtensionType.System) { + scannedExtensions.push(...await this.extensionsScannerService.scanAllExtensions(scannedOptions)); + } else if (type === ExtensionType.User) { + scannedExtensions.push(...await this.extensionsScannerService.scanUserExtensions(scannedOptions)); + } + scannedExtensions = type !== null ? scannedExtensions.filter(r => r.type === type) : scannedExtensions; + return Promise.all(scannedExtensions.map(extension => this.toLocalExtension(extension))); + } + + async scanUserExtensions(excludeOutdated: boolean): Promise { + const scannedExtensions = await this.extensionsScannerService.scanUserExtensions({ includeAllVersions: !excludeOutdated, includeInvalid: true }); + return Promise.all(scannedExtensions.map(extension => this.toLocalExtension(extension))); + } + + async extractUserExtension(extensionKey: ExtensionKey, zipPath: string, metadata: Metadata | undefined, token: CancellationToken): Promise { + const folderName = extensionKey.toString(); + const tempPath = path.join(this.extensionsScannerService.userExtensionsLocation.fsPath, `.${generateUuid()}`); + const extensionPath = path.join(this.extensionsScannerService.userExtensionsLocation.fsPath, folderName); + + try { + await pfs.Promises.rm(extensionPath); + } catch (error) { + throw new ExtensionManagementError(nls.localize('errorDeleting', "Unable to delete the existing folder '{0}' while installing the extension '{1}'. Please delete the folder manually and try again", extensionPath, extensionKey.id), ExtensionManagementErrorCode.Delete); + } + + await this.extractAtLocation(extensionKey, zipPath, tempPath, token); + await this.extensionsScannerService.updateMetadata(URI.file(tempPath), { ...metadata, installedTimestamp: Date.now() }); + + try { + await this.rename(extensionKey, tempPath, extensionPath, Date.now() + (2 * 60 * 1000) /* Retry for 2 minutes */); + this.logService.info('Renamed to', extensionPath); + } catch (error) { + try { + await pfs.Promises.rm(tempPath); + } catch (e) { /* ignore */ } + if (error.code === 'ENOTEMPTY') { + this.logService.info(`Rename failed because extension was installed by another source. So ignoring renaming.`, extensionKey.id); + } else { + this.logService.info(`Rename failed because of ${getErrorMessage(error)}. Deleted from extracted location`, tempPath); + throw error; + } + } + + return this.scanLocalExtension(URI.file(extensionPath), ExtensionType.User); + } + + async updateMetadata(local: ILocalExtension, metadata: Partial): Promise { + await this.extensionsScannerService.updateMetadata(local.location, metadata); + return this.scanLocalExtension(local.location, local.type); + } + + getUninstalledExtensions(): Promise> { + return this.withUninstalledExtensions(); + } + + async setUninstalled(...extensions: ILocalExtension[]): Promise { + const extensionKeys: ExtensionKey[] = extensions.map(e => ExtensionKey.create(e)); + await this.withUninstalledExtensions(uninstalled => { + extensionKeys.forEach(extensionKey => uninstalled[extensionKey.toString()] = true); + }); + } + + async setInstalled(extensionKey: ExtensionKey): Promise { + await this.withUninstalledExtensions(uninstalled => delete uninstalled[extensionKey.toString()]); + const userExtensions = await this.scanUserExtensions(true); + const localExtension = userExtensions.find(i => ExtensionKey.create(i).equals(extensionKey)) || null; + if (!localExtension) { + return null; + } + return this.updateMetadata(localExtension, { installedTimestamp: Date.now() }); + } + + async removeExtension(extension: ILocalExtension | IScannedExtension, type: string): Promise { + this.logService.trace(`Deleting ${type} extension from disk`, extension.identifier.id, extension.location.fsPath); + await pfs.Promises.rm(extension.location.fsPath); + this.logService.info('Deleted from disk', extension.identifier.id, extension.location.fsPath); + } + + async removeUninstalledExtension(extension: ILocalExtension | IScannedExtension): Promise { + await this.removeExtension(extension, 'uninstalled'); + await this.withUninstalledExtensions(uninstalled => delete uninstalled[ExtensionKey.create(extension).toString()]); + } + + private async withUninstalledExtensions(updateFn?: (uninstalled: IStringDictionary) => void): Promise> { + return this.uninstalledFileLimiter.queue(async () => { + let raw: string | undefined; + try { + raw = await pfs.Promises.readFile(this.uninstalledPath, 'utf8'); + } catch (err) { + if (err.code !== 'ENOENT') { + throw err; + } + } + + let uninstalled = {}; + if (raw) { + try { + uninstalled = JSON.parse(raw); + } catch (e) { /* ignore */ } + } + + if (updateFn) { + updateFn(uninstalled); + if (Object.keys(uninstalled).length) { + await pfs.Promises.writeFile(this.uninstalledPath, JSON.stringify(uninstalled)); + } else { + await pfs.Promises.rm(this.uninstalledPath); + } + } + + return uninstalled; + }); + } + + private async extractAtLocation(identifier: IExtensionIdentifier, zipPath: string, location: string, token: CancellationToken): Promise { + this.logService.trace(`Started extracting the extension from ${zipPath} to ${location}`); + + // Clean the location + try { + await pfs.Promises.rm(location); + } catch (e) { + throw new ExtensionManagementError(this.joinErrors(e).message, ExtensionManagementErrorCode.Delete); + } + + try { + await extract(zipPath, location, { sourcePath: 'extension', overwrite: true }, token); + this.logService.info(`Extracted extension to ${location}:`, identifier.id); + } catch (e) { + try { await pfs.Promises.rm(location); } catch (e) { /* Ignore */ } + let errorCode = ExtensionManagementErrorCode.Extract; + if (e instanceof ExtractError) { + if (e.type === 'CorruptZip') { + errorCode = ExtensionManagementErrorCode.CorruptZip; + } else if (e.type === 'Incomplete') { + errorCode = ExtensionManagementErrorCode.IncompleteZip; + } + } + throw new ExtensionManagementError(e.message, errorCode); + } + } + + private async rename(identifier: IExtensionIdentifier, extractPath: string, renamePath: string, retryUntil: number): Promise { + try { + await pfs.Promises.rename(extractPath, renamePath); + } catch (error) { + if (isWindows && error && error.code === 'EPERM' && Date.now() < retryUntil) { + this.logService.info(`Failed renaming ${extractPath} to ${renamePath} with 'EPERM' error. Trying again...`, identifier.id); + return this.rename(identifier, extractPath, renamePath, retryUntil); + } + throw new ExtensionManagementError(error.message || nls.localize('renameError', "Unknown error while renaming {0} to {1}", extractPath, renamePath), error.code || ExtensionManagementErrorCode.Rename); + } + } + + private async scanLocalExtension(location: URI, type: ExtensionType): Promise { + const scannedExtension = await this.extensionsScannerService.scanExistingExtension(location, type, { includeInvalid: true }); + if (scannedExtension) { + return this.toLocalExtension(scannedExtension); + } + throw new Error(nls.localize('cannot read', "Cannot read the extension from {0}", location.path)); + } + + private async toLocalExtension(extension: IScannedExtension): Promise { + const stat = await this.fileService.resolve(extension.location); + let readmeUrl: URI | undefined; + let changelogUrl: URI | undefined; + if (stat.children) { + readmeUrl = stat.children.find(({ name }) => /^readme(\.txt|\.md|)$/i.test(name))?.resource; + changelogUrl = stat.children.find(({ name }) => /^changelog(\.txt|\.md|)$/i.test(name))?.resource; + } + return { + identifier: extension.identifier, + type: extension.type, + isBuiltin: extension.isBuiltin || !!extension.metadata?.isBuiltin, + location: extension.location, + manifest: extension.manifest, + targetPlatform: extension.targetPlatform, + validations: extension.validations, + isValid: extension.isValid, + readmeUrl, + changelogUrl, + publisherDisplayName: extension.metadata?.publisherDisplayName || null, + publisherId: extension.metadata?.publisherId || null, + isMachineScoped: !!extension.metadata?.isMachineScoped, + isPreReleaseVersion: !!extension.metadata?.isPreReleaseVersion, + preRelease: !!extension.metadata?.preRelease, + installedTimestamp: extension.metadata?.installedTimestamp, + updated: !!extension.metadata?.updated, + }; + } + private async removeUninstalledExtensions(): Promise { + const uninstalled = await this.getUninstalledExtensions(); + const extensions = await this.extensionsScannerService.scanUserExtensions({ includeAllVersions: true, includeUninstalled: true, includeInvalid: true }); // All user extensions + const installed: Set = new Set(); + for (const e of extensions) { + if (!uninstalled[ExtensionKey.create(e).toString()]) { + installed.add(e.identifier.id.toLowerCase()); + } + } + const byExtension = groupByExtension(extensions, e => e.identifier); + await Promises.settled(byExtension.map(async e => { + const latest = e.sort((a, b) => semver.rcompare(a.manifest.version, b.manifest.version))[0]; + if (!installed.has(latest.identifier.id.toLowerCase())) { + await this.beforeRemovingExtension(await this.toLocalExtension(latest)); + } + })); + const toRemove = extensions.filter(e => uninstalled[ExtensionKey.create(e).toString()]); + await Promises.settled(toRemove.map(e => this.removeUninstalledExtension(e))); + } + + private async removeOutdatedExtensions(): Promise { + 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; + } + if (a.targetPlatform === targetPlatform) { + return -1; + } + return 1; + }).slice(1)).flat()); + + await Promises.settled(toRemove.map(extension => this.removeExtension(extension, 'outdated'))); + } + + private joinErrors(errorOrErrors: (Error | string) | (Array)): Error { + const errors = Array.isArray(errorOrErrors) ? errorOrErrors : [errorOrErrors]; + if (errors.length === 1) { + return errors[0] instanceof Error ? errors[0] : new Error(errors[0]); + } + return errors.reduce((previousValue: Error, currentValue: Error | string) => { + return new Error(`${previousValue.message}${previousValue.message ? ',' : ''}${currentValue instanceof Error ? currentValue.message : currentValue}`); + }, new Error('')); + } + +} + abstract class AbstractInstallExtensionTask extends AbstractExtensionTask implements IInstallExtensionTask { protected _operation = InstallOperation.Install; diff --git a/src/vs/platform/extensionManagement/node/extensionsScanner.ts b/src/vs/platform/extensionManagement/node/extensionsScanner.ts deleted file mode 100644 index 0e57ea12488..00000000000 --- a/src/vs/platform/extensionManagement/node/extensionsScanner.ts +++ /dev/null @@ -1,292 +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 { flatten } from 'vs/base/common/arrays'; -import { Promises, Queue } from 'vs/base/common/async'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { IStringDictionary } from 'vs/base/common/collections'; -import { getErrorMessage } from 'vs/base/common/errors'; -import { Disposable } from 'vs/base/common/lifecycle'; -import * as path from 'vs/base/common/path'; -import { isWindows } from 'vs/base/common/platform'; -import { joinPath } from 'vs/base/common/resources'; -import * as semver from 'vs/base/common/semver/semver'; -import { URI } from 'vs/base/common/uri'; -import { generateUuid } from 'vs/base/common/uuid'; -import * as pfs from 'vs/base/node/pfs'; -import { extract, ExtractError } from 'vs/base/node/zip'; -import { localize } from 'vs/nls'; -import { ExtensionManagementError, ExtensionManagementErrorCode, Metadata, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { ExtensionKey, groupByExtension } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { IExtensionsScannerService, IScannedExtension, ScanOptions } from 'vs/platform/extensionManagement/common/extensionsScannerService'; -import { ExtensionType, IExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { IFileService } from 'vs/platform/files/common/files'; -import { ILogService } from 'vs/platform/log/common/log'; - -export class ExtensionsScanner extends Disposable { - - private readonly uninstalledPath: string; - private readonly uninstalledFileLimiter: Queue; - - constructor( - private readonly beforeRemovingExtension: (e: ILocalExtension) => Promise, - @IFileService private readonly fileService: IFileService, - @IExtensionsScannerService private readonly extensionsScannerService: IExtensionsScannerService, - @ILogService private readonly logService: ILogService, - ) { - super(); - this.uninstalledPath = joinPath(this.extensionsScannerService.userExtensionsLocation, '.obsolete').fsPath; - this.uninstalledFileLimiter = new Queue(); - } - - async cleanUp(): Promise { - await this.removeUninstalledExtensions(); - await this.removeOutdatedExtensions(); - } - - async scanExtensions(type: ExtensionType | null): Promise { - const scannedOptions: ScanOptions = { includeInvalid: true }; - let scannedExtensions: IScannedExtension[] = []; - if (type === null || type === ExtensionType.System) { - scannedExtensions.push(...await this.extensionsScannerService.scanAllExtensions(scannedOptions)); - } else if (type === ExtensionType.User) { - scannedExtensions.push(...await this.extensionsScannerService.scanUserExtensions(scannedOptions)); - } - scannedExtensions = type !== null ? scannedExtensions.filter(r => r.type === type) : scannedExtensions; - return Promise.all(scannedExtensions.map(extension => this.toLocalExtension(extension))); - } - - async scanUserExtensions(excludeOutdated: boolean): Promise { - const scannedExtensions = await this.extensionsScannerService.scanUserExtensions({ includeAllVersions: !excludeOutdated, includeInvalid: true }); - return Promise.all(scannedExtensions.map(extension => this.toLocalExtension(extension))); - } - - async extractUserExtension(extensionKey: ExtensionKey, zipPath: string, metadata: Metadata | undefined, token: CancellationToken): Promise { - const folderName = extensionKey.toString(); - const tempPath = path.join(this.extensionsScannerService.userExtensionsLocation.fsPath, `.${generateUuid()}`); - const extensionPath = path.join(this.extensionsScannerService.userExtensionsLocation.fsPath, folderName); - - try { - await pfs.Promises.rm(extensionPath); - } catch (error) { - throw new ExtensionManagementError(localize('errorDeleting', "Unable to delete the existing folder '{0}' while installing the extension '{1}'. Please delete the folder manually and try again", extensionPath, extensionKey.id), ExtensionManagementErrorCode.Delete); - } - - await this.extractAtLocation(extensionKey, zipPath, tempPath, token); - await this.extensionsScannerService.updateMetadata(URI.file(tempPath), { ...metadata, installedTimestamp: Date.now() }); - - try { - await this.rename(extensionKey, tempPath, extensionPath, Date.now() + (2 * 60 * 1000) /* Retry for 2 minutes */); - this.logService.info('Renamed to', extensionPath); - } catch (error) { - try { - await pfs.Promises.rm(tempPath); - } catch (e) { /* ignore */ } - if (error.code === 'ENOTEMPTY') { - this.logService.info(`Rename failed because extension was installed by another source. So ignoring renaming.`, extensionKey.id); - } else { - this.logService.info(`Rename failed because of ${getErrorMessage(error)}. Deleted from extracted location`, tempPath); - throw error; - } - } - - return this.scanLocalExtension(URI.file(extensionPath), ExtensionType.User); - } - - async updateMetadata(local: ILocalExtension, metadata: Partial): Promise { - await this.extensionsScannerService.updateMetadata(local.location, metadata); - return this.scanLocalExtension(local.location, local.type); - } - - getUninstalledExtensions(): Promise> { - return this.withUninstalledExtensions(); - } - - async setUninstalled(...extensions: ILocalExtension[]): Promise { - const extensionKeys: ExtensionKey[] = extensions.map(e => ExtensionKey.create(e)); - await this.withUninstalledExtensions(uninstalled => { - extensionKeys.forEach(extensionKey => uninstalled[extensionKey.toString()] = true); - }); - } - - async setInstalled(extensionKey: ExtensionKey): Promise { - await this.withUninstalledExtensions(uninstalled => delete uninstalled[extensionKey.toString()]); - const userExtensions = await this.scanUserExtensions(true); - const localExtension = userExtensions.find(i => ExtensionKey.create(i).equals(extensionKey)) || null; - if (!localExtension) { - return null; - } - return this.updateMetadata(localExtension, { installedTimestamp: Date.now() }); - } - - async removeExtension(extension: ILocalExtension | IScannedExtension, type: string): Promise { - this.logService.trace(`Deleting ${type} extension from disk`, extension.identifier.id, extension.location.fsPath); - await pfs.Promises.rm(extension.location.fsPath); - this.logService.info('Deleted from disk', extension.identifier.id, extension.location.fsPath); - } - - async removeUninstalledExtension(extension: ILocalExtension | IScannedExtension): Promise { - await this.removeExtension(extension, 'uninstalled'); - await this.withUninstalledExtensions(uninstalled => delete uninstalled[ExtensionKey.create(extension).toString()]); - } - - private async withUninstalledExtensions(updateFn?: (uninstalled: IStringDictionary) => void): Promise> { - return this.uninstalledFileLimiter.queue(async () => { - let raw: string | undefined; - try { - raw = await pfs.Promises.readFile(this.uninstalledPath, 'utf8'); - } catch (err) { - if (err.code !== 'ENOENT') { - throw err; - } - } - - let uninstalled = {}; - if (raw) { - try { - uninstalled = JSON.parse(raw); - } catch (e) { /* ignore */ } - } - - if (updateFn) { - updateFn(uninstalled); - if (Object.keys(uninstalled).length) { - await pfs.Promises.writeFile(this.uninstalledPath, JSON.stringify(uninstalled)); - } else { - await pfs.Promises.rm(this.uninstalledPath); - } - } - - return uninstalled; - }); - } - - private async extractAtLocation(identifier: IExtensionIdentifier, zipPath: string, location: string, token: CancellationToken): Promise { - this.logService.trace(`Started extracting the extension from ${zipPath} to ${location}`); - - // Clean the location - try { - await pfs.Promises.rm(location); - } catch (e) { - throw new ExtensionManagementError(this.joinErrors(e).message, ExtensionManagementErrorCode.Delete); - } - - try { - await extract(zipPath, location, { sourcePath: 'extension', overwrite: true }, token); - this.logService.info(`Extracted extension to ${location}:`, identifier.id); - } catch (e) { - try { await pfs.Promises.rm(location); } catch (e) { /* Ignore */ } - let errorCode = ExtensionManagementErrorCode.Extract; - if (e instanceof ExtractError) { - if (e.type === 'CorruptZip') { - errorCode = ExtensionManagementErrorCode.CorruptZip; - } else if (e.type === 'Incomplete') { - errorCode = ExtensionManagementErrorCode.IncompleteZip; - } - } - throw new ExtensionManagementError(e.message, errorCode); - } - } - - private async rename(identifier: IExtensionIdentifier, extractPath: string, renamePath: string, retryUntil: number): Promise { - try { - await pfs.Promises.rename(extractPath, renamePath); - } catch (error) { - if (isWindows && error && error.code === 'EPERM' && Date.now() < retryUntil) { - this.logService.info(`Failed renaming ${extractPath} to ${renamePath} with 'EPERM' error. Trying again...`, identifier.id); - return this.rename(identifier, extractPath, renamePath, retryUntil); - } - throw new ExtensionManagementError(error.message || localize('renameError', "Unknown error while renaming {0} to {1}", extractPath, renamePath), error.code || ExtensionManagementErrorCode.Rename); - } - } - - private async scanLocalExtension(location: URI, type: ExtensionType): Promise { - const scannedExtension = await this.extensionsScannerService.scanExistingExtension(location, type, { includeInvalid: true }); - if (scannedExtension) { - return this.toLocalExtension(scannedExtension); - } - throw new Error(localize('cannot read', "Cannot read the extension from {0}", location.path)); - } - - private async toLocalExtension(extension: IScannedExtension): Promise { - const stat = await this.fileService.resolve(extension.location); - let readmeUrl: URI | undefined; - let changelogUrl: URI | undefined; - if (stat.children) { - readmeUrl = stat.children.find(({ name }) => /^readme(\.txt|\.md|)$/i.test(name))?.resource; - changelogUrl = stat.children.find(({ name }) => /^changelog(\.txt|\.md|)$/i.test(name))?.resource; - } - return { - identifier: extension.identifier, - type: extension.type, - isBuiltin: extension.isBuiltin || !!extension.metadata?.isBuiltin, - location: extension.location, - manifest: extension.manifest, - targetPlatform: extension.targetPlatform, - validations: extension.validations, - isValid: extension.isValid, - readmeUrl, - changelogUrl, - publisherDisplayName: extension.metadata?.publisherDisplayName || null, - publisherId: extension.metadata?.publisherId || null, - isMachineScoped: !!extension.metadata?.isMachineScoped, - isPreReleaseVersion: !!extension.metadata?.isPreReleaseVersion, - preRelease: !!extension.metadata?.preRelease, - installedTimestamp: extension.metadata?.installedTimestamp, - updated: !!extension.metadata?.updated, - }; - } - private async removeUninstalledExtensions(): Promise { - const uninstalled = await this.getUninstalledExtensions(); - const extensions = await this.extensionsScannerService.scanUserExtensions({ includeAllVersions: true, includeUninstalled: true, includeInvalid: true }); // All user extensions - const installed: Set = new Set(); - for (const e of extensions) { - if (!uninstalled[ExtensionKey.create(e).toString()]) { - installed.add(e.identifier.id.toLowerCase()); - } - } - const byExtension = groupByExtension(extensions, e => e.identifier); - await Promises.settled(byExtension.map(async e => { - const latest = e.sort((a, b) => semver.rcompare(a.manifest.version, b.manifest.version))[0]; - if (!installed.has(latest.identifier.id.toLowerCase())) { - await this.beforeRemovingExtension(await this.toLocalExtension(latest)); - } - })); - const toRemove = extensions.filter(e => uninstalled[ExtensionKey.create(e).toString()]); - await Promises.settled(toRemove.map(e => this.removeUninstalledExtension(e))); - } - - private async removeOutdatedExtensions(): Promise { - 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(...flatten(byExtension.map(p => p.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)))); - - await Promises.settled(toRemove.map(extension => this.removeExtension(extension, 'outdated'))); - } - - private joinErrors(errorOrErrors: (Error | string) | (Array)): Error { - const errors = Array.isArray(errorOrErrors) ? errorOrErrors : [errorOrErrors]; - if (errors.length === 1) { - return errors[0] instanceof Error ? errors[0] : new Error(errors[0]); - } - return errors.reduce((previousValue: Error, currentValue: Error | string) => { - return new Error(`${previousValue.message}${previousValue.message ? ',' : ''}${currentValue instanceof Error ? currentValue.message : currentValue}`); - }, new Error('')); - } - -} From 4ea02deebdd18be024846e06e00046025671cef1 Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 14 Apr 2022 11:04:35 +0200 Subject: [PATCH 073/245] ignore text document version whenever that document is marked as being created, fixes https://github.com/microsoft/vscode/issues/146964 --- .../src/singlefolder-tests/workspace.test.ts | 24 +++++++++++++++++++ .../api/common/extHostTypeConverters.ts | 15 ++++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index a0c072998aa..4337e8043c8 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -1008,6 +1008,30 @@ suite('vscode API - workspace', () => { assert.strictEqual(e.files[1].oldUri.toString(), file2.toString()); }); + test('WorkspaceEdit fails when creating then writing to file if file is open in the editor and is not empty #146964', async function () { + const file1 = await createRandomFile(); + + { + // prepare: open file in editor, make sure it has contents + const editor = await vscode.window.showTextDocument(file1); + const prepEdit = new vscode.WorkspaceEdit(); + prepEdit.insert(file1, new vscode.Position(0, 0), 'Hello Here And There'); + const status = await vscode.workspace.applyEdit(prepEdit); + + assert.ok(status); + assert.strictEqual(editor.document.getText(), 'Hello Here And There'); + assert.ok(vscode.window.activeTextEditor === editor); + } + + const we = new vscode.WorkspaceEdit(); + we.createFile(file1, { overwrite: true, ignoreIfExists: false }); + we.set(file1, [new vscode.TextEdit(new vscode.Range(new vscode.Position(0, 0), new vscode.Position(0, 0)), 'SOME TEXT')]); + const status = await vscode.workspace.applyEdit(we); + assert.ok(status); + assert.strictEqual(vscode.window.activeTextEditor!.document.getText(), 'SOME TEXT'); + + }); + test('Should send a single FileWillRenameEvent instead of separate events when moving multiple files at once#111867, 2/3', async function () { const event = new Promise(resolve => { diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 9dec8f6b163..d39678dd978 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -7,6 +7,7 @@ import { asArray, coalesce, isNonEmptyArray } from 'vs/base/common/arrays'; import { VSBuffer } from 'vs/base/common/buffer'; import * as htmlContent from 'vs/base/common/htmlContent'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ResourceSet } from 'vs/base/common/map'; import { marked } from 'vs/base/common/marked/marked'; import { parse } from 'vs/base/common/marshalling'; import { cloneAndChange } from 'vs/base/common/objects'; @@ -580,7 +581,17 @@ export namespace WorkspaceEdit { }; if (value instanceof types.WorkspaceEdit) { - for (let entry of value._allEntries()) { + + // collect all files that are to be created so that their version + // information (in case they exist as text model already) can be ignored + const toCreate = new ResourceSet(); + for (const entry of value._allEntries()) { + if (entry._type === types.FileEditType.File && URI.isUri(entry.to) && entry.from === undefined) { + toCreate.add(entry.to); + } + } + + for (const entry of value._allEntries()) { if (entry._type === types.FileEditType.File) { // file operation @@ -598,7 +609,7 @@ export namespace WorkspaceEdit { _type: extHostProtocol.WorkspaceEditType.Text, resource: entry.uri, edit: TextEdit.from(entry.edit), - modelVersionId: versionInfo?.getTextDocumentVersion(entry.uri), + modelVersionId: !toCreate.has(entry.uri) ? versionInfo?.getTextDocumentVersion(entry.uri) : undefined, metadata: entry.metadata }); } else if (entry._type === types.FileEditType.Cell) { From 8b04a1acf9e0aa6bfd43eb5b67d0d9c833779451 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 14 Apr 2022 11:30:43 +0200 Subject: [PATCH 074/245] lifecycle - show pending operations on shutdown in dialog --- .../electron-sandbox/desktop.main.ts | 3 +- src/vs/workbench/electron-sandbox/window.ts | 42 ++++++++++++------- .../localProcessExtensionHost.ts | 2 +- .../lifecycle/browser/lifecycleService.ts | 7 ++-- .../services/lifecycle/common/lifecycle.ts | 14 ++++++- .../electron-sandbox/lifecycleService.ts | 13 +++--- .../electron-browser/lifecycleService.test.ts | 4 +- .../electron-sandbox/nativeTextFileService.ts | 3 +- .../common/storedFileWorkingCopyManager.ts | 3 +- .../workingCopyBackupService.ts | 3 +- .../workingCopyHistoryService.ts | 3 +- .../test/browser/workbenchTestServices.ts | 6 ++- 12 files changed, 66 insertions(+), 37 deletions(-) diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts index 6a78e65d27c..1df19cda720 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { localize } from 'vs/nls'; import product from 'vs/platform/product/common/product'; import { INativeWindowConfiguration, zoomLevelToZoomFactor } from 'vs/platform/window/common/window'; import { Workbench } from 'vs/workbench/browser/workbench'; @@ -129,7 +130,7 @@ export class DesktopMain extends Disposable { private registerListeners(workbench: Workbench, storageService: NativeStorageService): void { // Workbench Lifecycle - this._register(workbench.onWillShutdown(event => event.join(storageService.close(), 'join.closeStorage'))); + this._register(workbench.onWillShutdown(event => event.join(storageService.close(), { id: 'join.closeStorage', label: localize('join.closeStorage', "Saving UI state") }))); this._register(workbench.onDidShutdown(() => this.dispose())); } diff --git a/src/vs/workbench/electron-sandbox/window.ts b/src/vs/workbench/electron-sandbox/window.ts index dc10761c214..82a3ef0a045 100644 --- a/src/vs/workbench/electron-sandbox/window.ts +++ b/src/vs/workbench/electron-sandbox/window.ts @@ -359,7 +359,7 @@ export class NativeWindow extends Disposable { // Progress for long running shutdown if (confirmed) { - this.progressOnShutdown(reason); + this.progressOnBeforeShutdown(reason); } return !confirmed; @@ -368,10 +368,10 @@ export class NativeWindow extends Disposable { } // Progress for long running shutdown - this.progressOnShutdown(reason); + this.progressOnBeforeShutdown(reason); } - private progressOnShutdown(reason: ShutdownReason): void { + private progressOnBeforeShutdown(reason: ShutdownReason): void { this.progressService.withProgress({ location: ProgressLocation.Window, // use window progress to not be too annoying about this operation delay: 800, // delay so that it only appears when operation takes a long time @@ -419,19 +419,29 @@ export class NativeWindow extends Disposable { }); } - private onWillShutdown({ reason, force }: WillShutdownEvent): void { - this.progressService.withProgress({ - location: ProgressLocation.Dialog, // use a dialog to prevent the user from making any more interactions now - buttons: [this.toForceShutdownLabel(reason)], // allow to force shutdown anyway - delay: 800, // delay so that it only appears when operation takes a long time - cancellable: false, // do not allow to cancel - sticky: true, // do not allow to dismiss - title: this.toShutdownLabel(reason, false) - }, () => { - return Event.toPromise(this.lifecycleService.onDidShutdown); // dismiss this dialog when we actually shutdown - }, () => { - force(); - }); + private onWillShutdown({ reason, force, joiners }: WillShutdownEvent): void { + + // Delay so that the dialog only appears after timeout + const shutdownDialogScheduler = new RunOnceScheduler(() => { + const pendingJoiners = joiners(); + + this.progressService.withProgress({ + location: ProgressLocation.Dialog, // use a dialog to prevent the user from making any more interactions now + buttons: [this.toForceShutdownLabel(reason)], // allow to force shutdown anyway + cancellable: false, // do not allow to cancel + sticky: true, // do not allow to dismiss + title: this.toShutdownLabel(reason, false), + detail: pendingJoiners.length > 0 ? localize('willShutdownDetail', "The following operations are still running: \n{0}", pendingJoiners.map(joiner => `- ${joiner.label}`).join('\n')) : undefined + }, () => { + return Event.toPromise(this.lifecycleService.onDidShutdown); // dismiss this dialog when we actually shutdown + }, () => { + force(); + }); + }, 1200); + shutdownDialogScheduler.schedule(); + + // Dispose scheduler when we actually shutdown + Event.once(this.lifecycleService.onDidShutdown)(() => shutdownDialogScheduler.dispose()); } private toShutdownLabel(reason: ShutdownReason, isError: boolean): string { diff --git a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts index 2f7e9b944f6..31e98e06a85 100644 --- a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts @@ -695,7 +695,7 @@ export class LocalProcessExtensionHost implements IExtensionHost { // to communicate this back to the main side to terminate the debug session if (this._isExtensionDevHost && !this._isExtensionDevTestFromCli && !this._isExtensionDevDebug && this._environmentService.debugExtensionHost.debugId) { this._extensionHostDebugService.terminateSession(this._environmentService.debugExtensionHost.debugId); - event.join(timeout(100 /* wait a bit for IPC to get delivered */), 'join.extensionDevelopment'); + event.join(timeout(100 /* wait a bit for IPC to get delivered */), { id: 'join.extensionDevelopment', label: nls.localize('join.extensionDevelopment', "Terminating extension debug session") }); } } } diff --git a/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts b/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts index 6915f6f035d..96c7fcbff3f 100644 --- a/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts +++ b/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts @@ -168,9 +168,10 @@ export class BrowserLifecycleService extends AbstractLifecycleService { const logService = this.logService; this._onWillShutdown.fire({ reason: ShutdownReason.QUIT, - token: CancellationToken.None, // Unsupported in web - join(promise, id) { - logService.error(`[lifecycle] Long running operations during shutdown are unsupported in the web (id: ${id})`); + joiners: () => [], // Unsupported in web + token: CancellationToken.None, // Unsupported in web + join(promise, joiner) { + logService.error(`[lifecycle] Long running operations during shutdown are unsupported in the web (id: ${joiner.id})`); }, force: () => { /* No-Op in web */ }, }); diff --git a/src/vs/workbench/services/lifecycle/common/lifecycle.ts b/src/vs/workbench/services/lifecycle/common/lifecycle.ts index 199577ebfde..11761f80115 100644 --- a/src/vs/workbench/services/lifecycle/common/lifecycle.ts +++ b/src/vs/workbench/services/lifecycle/common/lifecycle.ts @@ -65,6 +65,11 @@ export interface BeforeShutdownErrorEvent { readonly error: Error; } +export interface IWillShutdownEventJoiner { + id: string; + label: string; +} + /** * An event that is send out when the window closes. Clients have a chance to join the closing * by providing a promise from the join method. Returning a promise is useful in cases of long @@ -90,10 +95,15 @@ export interface WillShutdownEvent { * Allows to join the shutdown. The promise can be a long running operation but it * will block the application from closing. * - * @param id to identify the join operation in case it takes very long or never + * @param joiner to identify the join operation in case it takes very long or never * completes. */ - join(promise: Promise, id: string): void; + join(promise: Promise, joiner: IWillShutdownEventJoiner): void; + + /** + * Allows to access the joiners that have not finished joining this event. + */ + joiners(): IWillShutdownEventJoiner[]; /** * Allows to enforce the shutdown, even when there are diff --git a/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts b/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts index a02dbc84a53..9ad9e92efe9 100644 --- a/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts +++ b/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { handleVetos } from 'vs/platform/lifecycle/common/lifecycle'; -import { ShutdownReason, ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { ShutdownReason, ILifecycleService, IWillShutdownEventJoiner } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { ILogService } from 'vs/platform/log/common/log'; @@ -155,18 +155,19 @@ export class NativeLifecycleService extends AbstractLifecycleService { protected async handleWillShutdown(reason: ShutdownReason): Promise { const joiners: Promise[] = []; - const pendingJoiners = new Set(); + const pendingJoiners = new Set(); const cts = new CancellationTokenSource(); this._onWillShutdown.fire({ reason, token: cts.token, - join(promise, id) { + joiners: () => Array.from(pendingJoiners.values()), + join(promise, joiner) { joiners.push(promise); // Track promise completion - pendingJoiners.add(id); - promise.finally(() => pendingJoiners.delete(id)); + pendingJoiners.add(joiner); + promise.finally(() => pendingJoiners.delete(joiner)); }, force: () => { cts.dispose(true); @@ -174,7 +175,7 @@ export class NativeLifecycleService extends AbstractLifecycleService { }); const longRunningWillShutdownWarning = disposableTimeout(() => { - this.logService.warn(`[lifecycle] onWillShutdown is taking a long time, pending operations: ${Array.from(pendingJoiners).join(', ')}`); + this.logService.warn(`[lifecycle] onWillShutdown is taking a long time, pending operations: ${Array.from(pendingJoiners).map(joiner => joiner.id).join(', ')}`); }, NativeLifecycleService.WILL_SHUTDOWN_WARNING_DELAY); try { diff --git a/src/vs/workbench/services/lifecycle/test/electron-browser/lifecycleService.test.ts b/src/vs/workbench/services/lifecycle/test/electron-browser/lifecycleService.test.ts index 63cd2106537..4edcf36737f 100644 --- a/src/vs/workbench/services/lifecycle/test/electron-browser/lifecycleService.test.ts +++ b/src/vs/workbench/services/lifecycle/test/electron-browser/lifecycleService.test.ts @@ -132,7 +132,7 @@ suite('Lifecycleservice', function () { joinCalled = true; resolve(); - }), 'test'); + }), { id: 'test', label: 'test' }); }); await lifecycleService.handleWillShutdown(ShutdownReason.QUIT); @@ -148,7 +148,7 @@ suite('Lifecycleservice', function () { joinCalled = true; reject(new Error('Fail')); - }), 'test'); + }), { id: 'test', label: 'test' }); }); await lifecycleService.handleWillShutdown(ShutdownReason.QUIT); diff --git a/src/vs/workbench/services/textfile/electron-sandbox/nativeTextFileService.ts b/src/vs/workbench/services/textfile/electron-sandbox/nativeTextFileService.ts index d4f8852280b..94cd05ed2e3 100644 --- a/src/vs/workbench/services/textfile/electron-sandbox/nativeTextFileService.ts +++ b/src/vs/workbench/services/textfile/electron-sandbox/nativeTextFileService.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { localize } from 'vs/nls'; import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { AbstractTextFileService } from 'vs/workbench/services/textfile/browser/textFileService'; import { ITextFileService, ITextFileStreamContent, ITextFileContent, IReadTextFileOptions, TextFileEditorModelState, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; @@ -63,7 +64,7 @@ export class NativeTextFileService extends AbstractTextFileService { private registerListeners(): void { // Lifecycle - this.lifecycleService.onWillShutdown(event => event.join(this.onWillShutdown(), 'join.textFiles')); + this.lifecycleService.onWillShutdown(event => event.join(this.onWillShutdown(), { id: 'join.textFiles', label: localize('join.textFiles', "Saving text files") })); } private async onWillShutdown(): Promise { diff --git a/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopyManager.ts b/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopyManager.ts index b92776c6c5a..d7c4f83dcc0 100644 --- a/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopyManager.ts +++ b/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopyManager.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { localize } from 'vs/nls'; import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { StoredFileWorkingCopy, StoredFileWorkingCopyState, IStoredFileWorkingCopy, IStoredFileWorkingCopyModel, IStoredFileWorkingCopyModelFactory, IStoredFileWorkingCopyResolveOptions, IStoredFileWorkingCopySaveEvent as IBaseStoredFileWorkingCopySaveEvent } from 'vs/workbench/services/workingCopy/common/storedFileWorkingCopy'; @@ -207,7 +208,7 @@ export class StoredFileWorkingCopyManager // Lifecycle this.lifecycleService.onBeforeShutdown(event => event.veto(this.onBeforeShutdown(), 'veto.fileWorkingCopyManager')); - this.lifecycleService.onWillShutdown(event => event.join(this.onWillShutdown(), 'join.fileWorkingCopyManager')); + this.lifecycleService.onWillShutdown(event => event.join(this.onWillShutdown(), { id: 'join.fileWorkingCopyManager', label: localize('join.fileWorkingCopyManager', "Saving working copies") })); } private onBeforeShutdown(): boolean { diff --git a/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService.ts b/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService.ts index d4c07c0f67a..d9fd62ac9b4 100644 --- a/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService.ts +++ b/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { localize } from 'vs/nls'; import { WorkingCopyBackupService } from 'vs/workbench/services/workingCopy/common/workingCopyBackupService'; import { URI } from 'vs/base/common/uri'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -33,7 +34,7 @@ export class NativeWorkingCopyBackupService extends WorkingCopyBackupService { // Lifecycle: ensure to prolong the shutdown for as long // as pending backup operations have not finished yet. // Otherwise, we risk writing partial backups to disk. - this.lifecycleService.onWillShutdown(event => event.join(this.joinBackups(), 'join.workingCopyBackups')); + this.lifecycleService.onWillShutdown(event => event.join(this.joinBackups(), { id: 'join.workingCopyBackups', label: localize('join.workingCopyBackups', "Backup working copies") })); } } diff --git a/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyHistoryService.ts b/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyHistoryService.ts index a252865a59a..491aafe591d 100644 --- a/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyHistoryService.ts +++ b/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyHistoryService.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { localize } from 'vs/nls'; import { Limiter } from 'vs/base/common/async'; import { ILifecycleService, WillShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IFileService } from 'vs/platform/files/common/files'; @@ -65,7 +66,7 @@ export class NativeWorkingCopyHistoryService extends WorkingCopyHistoryService { } await Promise.all(promises); - })(), 'join.workingCopyHistory'); + })(), { id: 'join.workingCopyHistory', label: localize('join.workingCopyHistory', "Saving local history") }); } } diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 3dcb9b71147..b4690df3ec9 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -21,7 +21,7 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { IEditorOptions, IResourceEditorInput, IEditorModel, IResourceEditorInputIdentifier, ITextResourceEditorInput, ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { IUntitledTextEditorService, UntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import { IWorkspaceContextService, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; -import { ILifecycleService, ShutdownReason, StartupKind, LifecyclePhase, WillShutdownEvent, BeforeShutdownErrorEvent, InternalBeforeShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { ILifecycleService, ShutdownReason, StartupKind, LifecyclePhase, WillShutdownEvent, BeforeShutdownErrorEvent, InternalBeforeShutdownEvent, IWillShutdownEventJoiner } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { FileOperationEvent, IFileService, IFileStat, IFileStatResult, FileChangesEvent, IResolveFileOptions, ICreateFileOptions, IFileSystemProvider, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileType, IFileDeleteOptions, IFileOverwriteOptions, IFileWriteOptions, IFileOpenOptions, IFileStatWithMetadata, IResolveMetadataFileOptions, IWriteFileOptions, IReadFileOptions, IFileContent, IFileStreamContent, FileOperationError, IFileSystemProviderWithFileReadStreamCapability, IFileReadStreamOptions, IReadFileStreamOptions, IFileSystemProviderCapabilitiesChangeEvent, IFileStatWithPartialMetadata } from 'vs/platform/files/common/files'; import { IModelService } from 'vs/editor/common/services/model'; @@ -1227,6 +1227,7 @@ export class TestLifecycleService implements ILifecycleService { join: p => { this.shutdownJoiners.push(p); }, + joiners: () => [], force: () => { /* No-Op in tests */ }, token: CancellationToken.None, reason @@ -1261,10 +1262,11 @@ export class TestBeforeShutdownEvent implements InternalBeforeShutdownEvent { export class TestWillShutdownEvent implements WillShutdownEvent { value: Promise[] = []; + joiners = () => []; reason = ShutdownReason.CLOSE; token = CancellationToken.None; - join(promise: Promise, id: string): void { + join(promise: Promise, joiner: IWillShutdownEventJoiner): void { this.value.push(promise); } From ac6b490b2d09e4d6831f8fdaf480e4f42bffbdca Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 14 Apr 2022 11:34:22 +0200 Subject: [PATCH 075/245] retr (#147442) --- .../src/areas/extensions/extensions.test.ts | 19 +++++----- .../src/areas/workbench/localization.test.ts | 35 ++++++++++--------- test/smoke/src/utils.ts | 15 ++++++++ 3 files changed, 44 insertions(+), 25 deletions(-) diff --git a/test/smoke/src/areas/extensions/extensions.test.ts b/test/smoke/src/areas/extensions/extensions.test.ts index 9bf6dd79b07..de5b4fa91c6 100644 --- a/test/smoke/src/areas/extensions/extensions.test.ts +++ b/test/smoke/src/areas/extensions/extensions.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Application, Logger } from '../../../../automation'; -import { installAllHandlers } from '../../utils'; +import { installAllHandlers, retryWithRestart } from '../../utils'; export function setup(logger: Logger) { describe('Extensions', () => { @@ -13,18 +13,19 @@ export function setup(logger: Logger) { installAllHandlers(logger); it('install and enable vscode-smoketest-check extension', async function () { - this.retries(3); // https://github.com/microsoft/vscode/issues/146800 + this.timeout(2 * 60 * 1000); // https://github.com/microsoft/vscode/issues/146800 const app = this.app as Application; + await retryWithRestart(app, async () => { + await app.workbench.extensions.openExtensionsViewlet(); + await app.workbench.extensions.installExtension('ms-vscode.vscode-smoketest-check', true); - await app.workbench.extensions.openExtensionsViewlet(); - await app.workbench.extensions.installExtension('ms-vscode.vscode-smoketest-check', true); + // Close extension editor because keybindings dispatch is not working when web views are opened and focused + // https://github.com/microsoft/vscode/issues/110276 + await app.workbench.extensions.closeExtension('vscode-smoketest-check'); - // Close extension editor because keybindings dispatch is not working when web views are opened and focused - // https://github.com/microsoft/vscode/issues/110276 - await app.workbench.extensions.closeExtension('vscode-smoketest-check'); - - await app.workbench.quickaccess.runCommand('Smoke Test Check'); + await app.workbench.quickaccess.runCommand('Smoke Test Check'); + }); }); }); } diff --git a/test/smoke/src/areas/workbench/localization.test.ts b/test/smoke/src/areas/workbench/localization.test.ts index 61b354a22f3..2b8704b72ff 100644 --- a/test/smoke/src/areas/workbench/localization.test.ts +++ b/test/smoke/src/areas/workbench/localization.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Logger, Application } from '../../../../automation'; -import { installAllHandlers } from '../../utils'; +import { installAllHandlers, retryWithRestart } from '../../utils'; export function setup(logger: Logger) { @@ -14,27 +14,30 @@ export function setup(logger: Logger) { installAllHandlers(logger); it('starts with "DE" locale and verifies title and viewlets text is in German', async function () { - this.retries(3); // https://github.com/microsoft/vscode/issues/146800 + this.timeout(2 * 60 * 1000); // https://github.com/microsoft/vscode/issues/146800 const app = this.app as Application; - await app.workbench.extensions.openExtensionsViewlet(); - await app.workbench.extensions.installExtension('ms-ceintl.vscode-language-pack-de', false); - await app.restart({ extraArgs: ['--locale=DE'] }); - const result = await app.workbench.localization.getLocalizedStrings(); - const localeInfo = await app.workbench.localization.getLocaleInfo(); + await retryWithRestart(app, async () => { + await app.workbench.extensions.openExtensionsViewlet(); + await app.workbench.extensions.installExtension('ms-ceintl.vscode-language-pack-de', false); + await app.restart({ extraArgs: ['--locale=DE'] }); - if (localeInfo.locale === undefined || localeInfo.locale.toLowerCase() !== 'de') { - throw new Error(`The requested locale for VS Code was not German. The received value is: ${localeInfo.locale === undefined ? 'not set' : localeInfo.locale}`); - } + const result = await app.workbench.localization.getLocalizedStrings(); + const localeInfo = await app.workbench.localization.getLocaleInfo(); - if (localeInfo.language.toLowerCase() !== 'de') { - throw new Error(`The UI language is not German. It is ${localeInfo.language}`); - } + if (localeInfo.locale === undefined || localeInfo.locale.toLowerCase() !== 'de') { + throw new Error(`The requested locale for VS Code was not German. The received value is: ${localeInfo.locale === undefined ? 'not set' : localeInfo.locale}`); + } - if (result.open.toLowerCase() !== 'öffnen' || result.close.toLowerCase() !== 'schließen' || result.find.toLowerCase() !== 'finden') { - throw new Error(`Received wrong German localized strings: ${JSON.stringify(result, undefined, 0)}`); - } + if (localeInfo.language.toLowerCase() !== 'de') { + throw new Error(`The UI language is not German. It is ${localeInfo.language}`); + } + + if (result.open.toLowerCase() !== 'öffnen' || result.close.toLowerCase() !== 'schließen' || result.find.toLowerCase() !== 'finden') { + throw new Error(`Received wrong German localized strings: ${JSON.stringify(result, undefined, 0)}`); + } + }); }); }); } diff --git a/test/smoke/src/utils.ts b/test/smoke/src/utils.ts index 1272924181b..bfcbd5f9e18 100644 --- a/test/smoke/src/utils.ts +++ b/test/smoke/src/utils.ts @@ -149,6 +149,21 @@ export function timeout(i: number) { }); } +export async function retryWithRestart(app: Application, testFn: () => Promise, retries = 3, timeoutMs = 20000): Promise { + for (let i = 0; i < retries; i++) { + const result = await Promise.race([ + testFn().then(() => true, error => { throw error; }), + timeout(timeoutMs).then(() => false) + ]); + + if (result) { + return; + } + + await app.restart(); + } +} + export interface ITask { (): T; } From 8ced95d76a9a6be15609361a1467bc6a003c2a23 Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 14 Apr 2022 14:07:00 +0200 Subject: [PATCH 076/245] when showing peek, reveal the range including the "after line" so that the whole peek view is visible, https://github.com/microsoft/vscode/issues/147448 --- .../contrib/gotoSymbol/browser/peek/referencesWidget.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.ts b/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.ts index baee24e0c7b..2e28c88ff5e 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.ts @@ -259,7 +259,9 @@ export class ReferenceWidget extends peekView.PeekViewWidget { } override show(where: IRange) { - this.editor.revealRangeInCenterIfOutsideViewport(where, ScrollType.Smooth); + setTimeout(() => { + this.editor.revealRangeInCenterIfOutsideViewport({ ...where, endLineNumber: where.startLineNumber + 1, endColumn: 1 }, ScrollType.Smooth); + }); super.show(where, this.layoutData.heightInLines || 18); } From c4dd43dd793ecc34c5e831b88e35d2fbac6cfd38 Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 14 Apr 2022 14:53:24 +0200 Subject: [PATCH 077/245] :lipstick: --- .../api/browser/mainThreadLanguageFeatures.ts | 2 +- .../workbench/api/common/extHost.protocol.ts | 2 +- .../api/common/extHostLanguageFeatures.ts | 64 +++++++++---------- 3 files changed, 32 insertions(+), 36 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 95716c771fb..3b5e9139ca3 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -487,7 +487,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread }; } - $registerSuggestSupport(handle: number, selector: IDocumentFilterDto[], triggerCharacters: string[], supportsResolveDetails: boolean, displayName: string): void { + $registerCompletionsProvider(handle: number, selector: IDocumentFilterDto[], triggerCharacters: string[], supportsResolveDetails: boolean, displayName: string): void { const provider: languages.CompletionItemProvider = { triggerCharacters, _debugDisplayName: displayName, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 53a77dc4d28..5b80a5ac3a7 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -378,7 +378,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $registerDocumentSemanticTokensProvider(handle: number, selector: IDocumentFilterDto[], legend: languages.SemanticTokensLegend, eventHandle: number | undefined): void; $emitDocumentSemanticTokensEvent(eventHandle: number): void; $registerDocumentRangeSemanticTokensProvider(handle: number, selector: IDocumentFilterDto[], legend: languages.SemanticTokensLegend): void; - $registerSuggestSupport(handle: number, selector: IDocumentFilterDto[], triggerCharacters: string[], supportsResolveDetails: boolean, displayName: string): void; + $registerCompletionsProvider(handle: number, selector: IDocumentFilterDto[], triggerCharacters: string[], supportsResolveDetails: boolean, displayName: string): void; $registerInlineCompletionsSupport(handle: number, selector: IDocumentFilterDto[]): void; $registerSignatureHelpProvider(handle: number, selector: IDocumentFilterDto[], metadata: ISignatureHelpProviderMetadataDto): void; $registerInlayHintsProvider(handle: number, selector: IDocumentFilterDto[], supportsResolve: boolean, eventHandle: number | undefined, displayName: string | undefined): void; diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index c53758c8946..ef72e012647 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -42,13 +42,10 @@ import { Dto } from 'vs/workbench/services/extensions/common/proxyIdentifier'; class DocumentSymbolAdapter { - private _documents: ExtHostDocuments; - private _provider: vscode.DocumentSymbolProvider; - - constructor(documents: ExtHostDocuments, provider: vscode.DocumentSymbolProvider) { - this._documents = documents; - this._provider = provider; - } + constructor( + private readonly _documents: ExtHostDocuments, + private readonly _provider: vscode.DocumentSymbolProvider + ) { } async provideDocumentSymbols(resource: URI, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); @@ -256,7 +253,7 @@ class HoverAdapter { private readonly _provider: vscode.HoverProvider, ) { } - public async provideHover(resource: URI, position: IPosition, token: CancellationToken): Promise { + async provideHover(resource: URI, position: IPosition, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); const pos = typeConvert.Position.to(position); @@ -464,7 +461,7 @@ class CodeActionAdapter { return { cacheId, actions }; } - public async resolveCodeAction(id: extHostProtocol.ChainedCacheId, token: CancellationToken): Promise { + async resolveCodeAction(id: extHostProtocol.ChainedCacheId, token: CancellationToken): Promise { const [sessionId, itemId] = id; const item = this._cache.get(sessionId, itemId); if (!item || CodeActionAdapter._isCommand(item)) { @@ -479,7 +476,7 @@ class CodeActionAdapter { : undefined; } - public releaseCodeActions(cachedId: number): void { + releaseCodeActions(cachedId: number): void { this._disposables.get(cachedId)?.dispose(); this._disposables.delete(cachedId); this._cache.delete(cachedId); @@ -697,8 +694,8 @@ class RenameAdapter { class SemanticTokensPreviousResult { constructor( - public readonly resultId: string | undefined, - public readonly tokens?: Uint32Array, + readonly resultId: string | undefined, + readonly tokens?: Uint32Array, ) { } } @@ -849,8 +846,7 @@ export class DocumentRangeSemanticTokensAdapter { constructor( private readonly _documents: ExtHostDocuments, private readonly _provider: vscode.DocumentRangeSemanticTokensProvider, - ) { - } + ) { } async provideDocumentRangeSemanticTokens(resource: URI, range: IRange, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); @@ -870,7 +866,7 @@ export class DocumentRangeSemanticTokensAdapter { } } -class SuggestAdapter { +class CompletionsAdapter { static supportsResolving(provider: vscode.CompletionItemProvider): boolean { return typeof provider.resolveCompletionItem === 'function'; @@ -915,7 +911,7 @@ class SuggestAdapter { const list = Array.isArray(itemsOrList) ? new CompletionList(itemsOrList) : itemsOrList; // keep result for providers that support resolving - const pid: number = SuggestAdapter.supportsResolving(this._provider) ? this._cache.add(list.items) : this._cache.add([]); + const pid: number = CompletionsAdapter.supportsResolving(this._provider) ? this._cache.add(list.items) : this._cache.add([]); const disposables = new DisposableStore(); this._disposables.set(pid, disposables); @@ -1027,13 +1023,13 @@ class SuggestAdapter { } class InlineCompletionAdapterBase { - public async provideInlineCompletions(resource: URI, position: IPosition, context: languages.InlineCompletionContext, token: CancellationToken): Promise { + async provideInlineCompletions(resource: URI, position: IPosition, context: languages.InlineCompletionContext, token: CancellationToken): Promise { return undefined; } - public disposeCompletions(pid: number): void { } + disposeCompletions(pid: number): void { } - public handleDidShowCompletionItem(pid: number, idx: number): void { } + handleDidShowCompletionItem(pid: number, idx: number): void { } } class InlineCompletionAdapter extends InlineCompletionAdapterBase { @@ -1048,7 +1044,7 @@ class InlineCompletionAdapter extends InlineCompletionAdapterBase { super(); } - public override async provideInlineCompletions(resource: URI, position: IPosition, context: languages.InlineCompletionContext, token: CancellationToken): Promise { + override async provideInlineCompletions(resource: URI, position: IPosition, context: languages.InlineCompletionContext, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); const pos = typeConvert.Position.to(position); @@ -1108,7 +1104,7 @@ class InlineCompletionAdapter extends InlineCompletionAdapterBase { }; } - public override disposeCompletions(pid: number) { + override disposeCompletions(pid: number) { this._cache.delete(pid); const d = this._disposables.get(pid); if (d) { @@ -1117,7 +1113,7 @@ class InlineCompletionAdapter extends InlineCompletionAdapterBase { this._disposables.delete(pid); } - public override handleDidShowCompletionItem(pid: number, idx: number): void { + override handleDidShowCompletionItem(pid: number, idx: number): void { const completionItem = this._cache.get(pid, idx); if (completionItem) { InlineCompletionController.get(this._provider).fireOnDidShowCompletionItem({ @@ -1149,7 +1145,7 @@ class InlineCompletionAdapterNew extends InlineCompletionAdapterBase { [languages.InlineCompletionTriggerKind.Explicit]: InlineCompletionTriggerKindNew.Invoke, }; - public override async provideInlineCompletions(resource: URI, position: IPosition, context: languages.InlineCompletionContext, token: CancellationToken): Promise { + override async provideInlineCompletions(resource: URI, position: IPosition, context: languages.InlineCompletionContext, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); const pos = typeConvert.Position.to(position); @@ -1218,12 +1214,12 @@ class InlineCompletionAdapterNew extends InlineCompletionAdapterBase { }; } - public override disposeCompletions(pid: number) { + override disposeCompletions(pid: number) { const data = this._references.disposeReferenceId(pid); data?.dispose(); } - public override handleDidShowCompletionItem(pid: number, idx: number): void { + override handleDidShowCompletionItem(pid: number, idx: number): void { const completionItem = this._references.get(pid)?.items[idx]; if (completionItem) { if (this._provider.handleDidShowCompletionItem && this.isAdditionProposedApiEnabled) { @@ -1257,7 +1253,7 @@ class ReferenceMap { export class InlineCompletionController implements vscode.InlineCompletionController { private static readonly map = new WeakMap, InlineCompletionController>(); - public static get(provider: vscode.InlineCompletionItemProvider): InlineCompletionController { + static get(provider: vscode.InlineCompletionItemProvider): InlineCompletionController { let existing = InlineCompletionController.map.get(provider); if (!existing) { existing = new InlineCompletionController(); @@ -1267,9 +1263,9 @@ export class InlineCompletionController i } private readonly _onDidShowCompletionItemEmitter = new Emitter>(); - public readonly onDidShowCompletionItem: vscode.Event> = this._onDidShowCompletionItemEmitter.event; + readonly onDidShowCompletionItem: vscode.Event> = this._onDidShowCompletionItemEmitter.event; - public fireOnDidShowCompletionItem(event: vscode.InlineCompletionItemDidShowEvent): void { + fireOnDidShowCompletionItem(event: vscode.InlineCompletionItemDidShowEvent): void { this._onDidShowCompletionItemEmitter.fire(event); } } @@ -1774,7 +1770,7 @@ class DocumentOnDropAdapter { type Adapter = DocumentSymbolAdapter | CodeLensAdapter | DefinitionAdapter | HoverAdapter | DocumentHighlightAdapter | ReferenceAdapter | CodeActionAdapter | DocumentFormattingAdapter | RangeFormattingAdapter | OnTypeFormattingAdapter | NavigateTypeAdapter | RenameAdapter - | SuggestAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter + | CompletionsAdapter | SignatureHelpAdapter | LinkProviderAdapter | ImplementationAdapter | TypeDefinitionAdapter | ColorProviderAdapter | FoldingProviderAdapter | DeclarationAdapter | SelectionRangeAdapter | CallHierarchyAdapter | TypeHierarchyAdapter | DocumentSemanticTokensAdapter | DocumentRangeSemanticTokensAdapter @@ -2194,21 +2190,21 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF // --- suggestion registerCompletionItemProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, triggerCharacters: string[]): vscode.Disposable { - const handle = this._addNewAdapter(new SuggestAdapter(this._documents, this._commands.converter, provider, this._apiDeprecation, extension), extension); - this._proxy.$registerSuggestSupport(handle, this._transformDocumentSelector(selector), triggerCharacters, SuggestAdapter.supportsResolving(provider), `${extension.identifier.value}(${triggerCharacters.join('')})`); + const handle = this._addNewAdapter(new CompletionsAdapter(this._documents, this._commands.converter, provider, this._apiDeprecation, extension), extension); + this._proxy.$registerCompletionsProvider(handle, this._transformDocumentSelector(selector), triggerCharacters, CompletionsAdapter.supportsResolving(provider), `${extension.identifier.value}(${triggerCharacters.join('')})`); return this._createDisposable(handle); } $provideCompletionItems(handle: number, resource: UriComponents, position: IPosition, context: languages.CompletionContext, token: CancellationToken): Promise { - return this._withAdapter(handle, SuggestAdapter, adapter => adapter.provideCompletionItems(URI.revive(resource), position, context, token), undefined, token); + return this._withAdapter(handle, CompletionsAdapter, adapter => adapter.provideCompletionItems(URI.revive(resource), position, context, token), undefined, token); } $resolveCompletionItem(handle: number, id: extHostProtocol.ChainedCacheId, token: CancellationToken): Promise { - return this._withAdapter(handle, SuggestAdapter, adapter => adapter.resolveCompletionItem(id, token), undefined, token); + return this._withAdapter(handle, CompletionsAdapter, adapter => adapter.resolveCompletionItem(id, token), undefined, token); } $releaseCompletionItems(handle: number, id: number): void { - this._withAdapter(handle, SuggestAdapter, adapter => adapter.releaseCompletionItems(id), undefined, undefined); + this._withAdapter(handle, CompletionsAdapter, adapter => adapter.releaseCompletionItems(id), undefined, undefined); } // --- ghost test From 35c34e1a9ab78f6987d20c34d641e4121f47b225 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 14 Apr 2022 15:08:28 +0200 Subject: [PATCH 078/245] add `TabGroupChangeEvent`-event which is similar to the `TabChangeEvent` (#147443) --- .../workbench/api/common/extHostEditorTabs.ts | 21 ++++++- .../test/browser/extHostEditorTabs.test.ts | 56 ++++++++++++++++++- src/vscode-dts/vscode.proposed.tabs.d.ts | 22 +++++++- 3 files changed, 93 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/api/common/extHostEditorTabs.ts b/src/vs/workbench/api/common/extHostEditorTabs.ts index e4b2d776732..292cd429c3f 100644 --- a/src/vs/workbench/api/common/extHostEditorTabs.ts +++ b/src/vs/workbench/api/common/extHostEditorTabs.ts @@ -12,6 +12,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { CustomEditorTabInput, NotebookDiffEditorTabInput, NotebookEditorTabInput, TerminalEditorTabInput, TextDiffTabInput, TextTabInput, ViewColumn, WebviewEditorTabInput } from 'vs/workbench/api/common/extHostTypes'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { assertIsDefined } from 'vs/base/common/types'; +import { diffSets } from 'vs/base/common/collections'; export interface IExtHostEditorTabs extends IExtHostEditorTabsShape { readonly _serviceBrand: undefined; @@ -213,7 +214,7 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs { private readonly _proxy: MainThreadEditorTabsShape; private readonly _onDidChangeTabs = new Emitter(); - private readonly _onDidChangeTabGroups = new Emitter(); + private readonly _onDidChangeTabGroups = new Emitter(); // Have to use ! because this gets initialized via an RPC proxy private _activeGroupId!: number; @@ -271,8 +272,22 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs { $acceptEditorTabModel(tabGroups: IEditorTabGroupDto[]): void { + const groupIdsBefore = new Set(this._extHostTabGroups.map(group => group.groupId)); + const groupIdsAfter = new Set(tabGroups.map(dto => dto.groupId)); + const diff = diffSets(groupIdsBefore, groupIdsAfter); + + const closed: vscode.TabGroup[] = this._extHostTabGroups.filter(group => diff.removed.includes(group.groupId)).map(group => group.apiObject); + const opened: vscode.TabGroup[] = []; + const changed: vscode.TabGroup[] = []; + + this._extHostTabGroups = tabGroups.map(tabGroup => { const group = new ExtHostEditorTabGroup(tabGroup, this._proxy, () => this._activeGroupId); + if (diff.added.includes(group.groupId)) { + opened.push(group.apiObject); + } else { + changed.push(group.apiObject); + } return group; }); @@ -281,7 +296,7 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs { if (activeTabGroupId !== undefined && this._activeGroupId !== activeTabGroupId) { this._activeGroupId = activeTabGroupId; } - this._onDidChangeTabGroups.fire(this._extHostTabGroups.map(g => g.apiObject)); + this._onDidChangeTabGroups.fire(Object.freeze({ opened, closed, changed })); } $acceptTabGroupUpdate(groupDto: IEditorTabGroupDto) { @@ -293,7 +308,7 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs { if (groupDto.isActive) { this._activeGroupId = groupDto.groupId; } - this._onDidChangeTabGroups.fire([group.apiObject]); + this._onDidChangeTabGroups.fire(Object.freeze({ changed: [group.apiObject], opened: [], closed: [] })); } $acceptTabOperation(operation: TabOperation) { diff --git a/src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts b/src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts index 1f7d249f197..a2076d386d2 100644 --- a/src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts +++ b/src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts @@ -7,7 +7,7 @@ import type * as vscode from 'vscode'; import assert = require('assert'); import { URI } from 'vs/base/common/uri'; import { mock } from 'vs/base/test/common/mock'; -import { IEditorTabDto, MainThreadEditorTabsShape, TabInputKind, TabModelOperationKind, TextInputDto } from 'vs/workbench/api/common/extHost.protocol'; +import { IEditorTabDto, IEditorTabGroupDto, MainThreadEditorTabsShape, TabInputKind, TabModelOperationKind, TextInputDto } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostEditorTabs } from 'vs/workbench/api/common/extHostEditorTabs'; import { SingleProxyRPCProtocol } from 'vs/workbench/api/test/common/testRPCProtocol'; import { TextTabInput } from 'vs/workbench/api/common/extHostTypes'; @@ -126,6 +126,60 @@ suite('ExtHostEditorTabs', function () { assert.strictEqual(count, 1); }); + test('Check TabGroupChangeEvent properties', function () { + const extHostEditorTabs = new ExtHostEditorTabs( + SingleProxyRPCProtocol(new class extends mock() { + // override/implement $moveTab or $closeTab + }) + ); + + const group1Data: IEditorTabGroupDto = { + isActive: true, + viewColumn: 0, + groupId: 12, + tabs: [] + }; + const group2Data: IEditorTabGroupDto = { ...group1Data, groupId: 13 }; + + const events: vscode.TabGroupChangeEvent[] = []; + extHostEditorTabs.tabGroups.onDidChangeTabGroups(e => events.push(e)); + // OPEN + extHostEditorTabs.$acceptEditorTabModel([group1Data]); + assert.deepStrictEqual(events, [{ + changed: [], + closed: [], + opened: [extHostEditorTabs.tabGroups.activeTabGroup] + }]); + + // OPEN, CHANGE + events.length = 0; + extHostEditorTabs.$acceptEditorTabModel([{ ...group1Data, isActive: false }, group2Data]); + assert.deepStrictEqual(events, [{ + changed: [extHostEditorTabs.tabGroups.all[0]], + closed: [], + opened: [extHostEditorTabs.tabGroups.all[1]] + }]); + + // CHANGE + events.length = 0; + extHostEditorTabs.$acceptEditorTabModel([group1Data, { ...group2Data, isActive: false }]); + assert.deepStrictEqual(events, [{ + changed: extHostEditorTabs.tabGroups.all, + closed: [], + opened: [] + }]); + + // CLOSE, CHANGE + events.length = 0; + const oldActiveGroup = extHostEditorTabs.tabGroups.activeTabGroup; + extHostEditorTabs.$acceptEditorTabModel([group2Data]); + assert.deepStrictEqual(events, [{ + changed: extHostEditorTabs.tabGroups.all, + closed: [oldActiveGroup], + opened: [] + }]); + }); + test('Ensure reference equality for activeTab and activeGroup', function () { const extHostEditorTabs = new ExtHostEditorTabs( SingleProxyRPCProtocol(new class extends mock() { diff --git a/src/vscode-dts/vscode.proposed.tabs.d.ts b/src/vscode-dts/vscode.proposed.tabs.d.ts index 273441b617e..381506507b6 100644 --- a/src/vscode-dts/vscode.proposed.tabs.d.ts +++ b/src/vscode-dts/vscode.proposed.tabs.d.ts @@ -161,6 +161,25 @@ declare module 'vscode' { readonly changed: readonly Tab[]; } + /** + * An event describing changes to tab groups. + */ + export interface TabGroupChangeEvent { + /** + * Tab groups that have been opened. + */ + readonly opened: readonly TabGroup[]; + /** + * Tab groups that have been closed. + */ + readonly closed: readonly TabGroup[]; + /** + * Tab groups that have changed, e.g have changed + * their {@link TabGroup.isActive active} state. + */ + readonly changed: readonly TabGroup[]; + } + /** * Represents a group of tabs. A tab group itself consists of multiple tab */ @@ -209,8 +228,7 @@ declare module 'vscode' { /** * An {@link Event event} which fires when {@link TabGroup tab groups} have changed. */ - // TODO@API consider `TabGroupChangeEvent` similar to `TabChangeEvent` - readonly onDidChangeTabGroups: Event; + readonly onDidChangeTabGroups: Event; /** * An {@link Event event} which fires when {@link Tab tabs} have changed. From db30f3d981b6f7af1073bf8053b397fd7c5b7918 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Thu, 14 Apr 2022 09:43:53 -0400 Subject: [PATCH 079/245] Change `TabChangeEvent` variable names --- .../workbench/api/common/extHostEditorTabs.ts | 24 +++++++++---------- src/vscode-dts/vscode.proposed.tabs.d.ts | 16 +++++++++---- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/api/common/extHostEditorTabs.ts b/src/vs/workbench/api/common/extHostEditorTabs.ts index 292cd429c3f..6fe404878bb 100644 --- a/src/vs/workbench/api/common/extHostEditorTabs.ts +++ b/src/vs/workbench/api/common/extHostEditorTabs.ts @@ -321,26 +321,26 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs { // Construct the tab change event based on the operation switch (operation.kind) { case TabModelOperationKind.TAB_OPEN: - this._onDidChangeTabs.fire({ - added: [tab.apiObject], - removed: [], + this._onDidChangeTabs.fire(Object.freeze({ + opened: [tab.apiObject], + closed: [], changed: [] - }); + })); return; case TabModelOperationKind.TAB_CLOSE: - this._onDidChangeTabs.fire({ - added: [], - removed: [tab.apiObject], + this._onDidChangeTabs.fire(Object.freeze({ + opened: [], + closed: [tab.apiObject], changed: [] - }); + })); return; case TabModelOperationKind.TAB_MOVE: case TabModelOperationKind.TAB_UPDATE: - this._onDidChangeTabs.fire({ - added: [], - removed: [], + this._onDidChangeTabs.fire(Object.freeze({ + opened: [], + closed: [], changed: [tab.apiObject] - }); + })); return; } } diff --git a/src/vscode-dts/vscode.proposed.tabs.d.ts b/src/vscode-dts/vscode.proposed.tabs.d.ts index 381506507b6..1029de92ac1 100644 --- a/src/vscode-dts/vscode.proposed.tabs.d.ts +++ b/src/vscode-dts/vscode.proposed.tabs.d.ts @@ -154,10 +154,18 @@ declare module 'vscode' { } export interface TabChangeEvent { - // TODO@API consider: opened - readonly added: readonly Tab[]; - // TODO@API consider: closed (aligns with TabGroups.close(...)) - readonly removed: readonly Tab[]; + /** + * The tabs that have been opened + */ + readonly opened: readonly Tab[]; + /** + * The tabs that have been closed + */ + readonly closed: readonly Tab[]; + /** + * Tabs that have changed, e.g have changed + * their {@link Tab.isActive active} state. + */ readonly changed: readonly Tab[]; } From 8b5f0cdd244d703e5372cdc0c8df2b9778e0edde Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 14 Apr 2022 16:11:13 +0200 Subject: [PATCH 080/245] local history - store entries periodically too --- .../services/lifecycle/common/lifecycle.ts | 2 +- .../common/workingCopyHistoryService.ts | 51 ++++++++++---- .../workingCopyHistoryService.ts | 69 +++++++++++++------ 3 files changed, 85 insertions(+), 37 deletions(-) diff --git a/src/vs/workbench/services/lifecycle/common/lifecycle.ts b/src/vs/workbench/services/lifecycle/common/lifecycle.ts index 11761f80115..a8c2c9e2cd6 100644 --- a/src/vs/workbench/services/lifecycle/common/lifecycle.ts +++ b/src/vs/workbench/services/lifecycle/common/lifecycle.ts @@ -180,7 +180,7 @@ export const enum LifecyclePhase { Eventually = 4 } -export function LifecyclePhaseToString(phase: LifecyclePhase) { +export function LifecyclePhaseToString(phase: LifecyclePhase): string { switch (phase) { case LifecyclePhase.Starting: return 'Starting'; case LifecyclePhase.Ready: return 'Ready'; diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyHistoryService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyHistoryService.ts index c38a73c7699..667440f7070 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyHistoryService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyHistoryService.ts @@ -76,7 +76,10 @@ export class WorkingCopyHistoryModel { private historyEntriesNameMatcher: RegExp | undefined = undefined; - private shouldStore: boolean = false; + private versionId = 0; + private storedVersionId = this.versionId; + + private readonly storeLimiter = new Limiter(1); constructor( workingCopyResource: URI, @@ -170,8 +173,8 @@ export class WorkingCopyHistoryModel { }; this.entries.push(entry); - // Mark as in need to be stored to disk - this.shouldStore = true; + // Update version ID of model to use for storing later + this.versionId++; // Events this.entryAddedEmitter.fire({ entry }); @@ -188,8 +191,8 @@ export class WorkingCopyHistoryModel { // Update entry entry.timestamp = timestamp; - // Mark as in need to be stored to disk - this.shouldStore = true; + // Update version ID of model to use for storing later + this.versionId++; // Events this.entryReplacedEmitter.fire({ entry }); @@ -217,8 +220,8 @@ export class WorkingCopyHistoryModel { // Remove from model this.entries.splice(index, 1); - // Mark as in need to be stored to disk - this.shouldStore = true; + // Update version ID of model to use for storing later + this.versionId++; // Events this.entryRemovedEmitter.fire({ entry }); @@ -248,8 +251,8 @@ export class WorkingCopyHistoryModel { // Update entry entry.source = properties.source; - // Mark as in need to be stored to disk - this.shouldStore = true; + // Update version ID of model to use for storing later + this.versionId++; // Events this.entryChangedEmitter.fire({ entry }); @@ -384,12 +387,29 @@ export class WorkingCopyHistoryModel { } async store(token: CancellationToken): Promise { - const historyEntriesFolder = assertIsDefined(this.historyEntriesFolder); - - if (!this.shouldStore) { - return; // fast return to avoid disk access when nothing changed + if (!this.shouldStore()) { + return; } + // Use a `Limiter` to prevent multiple `store` operations + // potentially running at the same time + + await this.storeLimiter.queue(async () => { + if (token.isCancellationRequested || !this.shouldStore()) { + return; + } + + return this.doStore(token); + }); + } + + private shouldStore(): boolean { + return this.storedVersionId !== this.versionId; + } + + private async doStore(token: CancellationToken): Promise { + const historyEntriesFolder = assertIsDefined(this.historyEntriesFolder); + // Make sure to await resolving when persisting await this.resolveEntriesOnce(); @@ -401,6 +421,7 @@ export class WorkingCopyHistoryModel { await this.cleanUpEntries(); // Without entries, remove the history folder + const storedVersion = this.versionId; if (this.entries.length === 0) { try { await this.fileService.del(historyEntriesFolder, { recursive: true }); @@ -414,8 +435,8 @@ export class WorkingCopyHistoryModel { await this.writeEntriesFile(); } - // Mark as being up to date on disk - this.shouldStore = false; + // Mark as stored version + this.storedVersionId = storedVersion; } private async cleanUpEntries(): Promise { diff --git a/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyHistoryService.ts b/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyHistoryService.ts index 491aafe591d..55167103df8 100644 --- a/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyHistoryService.ts +++ b/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyHistoryService.ts @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { Limiter } from 'vs/base/common/async'; +import { Event } from 'vs/base/common/event'; +import { Limiter, RunOnceScheduler } from 'vs/base/common/async'; import { ILifecycleService, WillShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IFileService } from 'vs/platform/files/common/files'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; @@ -16,11 +17,17 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IWorkingCopyHistoryModelOptions, WorkingCopyHistoryService } from 'vs/workbench/services/workingCopy/common/workingCopyHistoryService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkingCopyHistoryService, MAX_PARALLEL_HISTORY_IO_OPS } from 'vs/workbench/services/workingCopy/common/workingCopyHistory'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; export class NativeWorkingCopyHistoryService extends WorkingCopyHistoryService { + private static readonly STORE_ALL_INTERVAL = 5 * 60 * 1000; // 5min + private readonly isRemotelyStored = typeof this.environmentService.remoteAuthority === 'string'; + private readonly storeAllCts = this._register(new CancellationTokenSource()); + private readonly storeAllScheduler = this._register(new RunOnceScheduler(() => this.storeAll(this.storeAllCts.token), NativeWorkingCopyHistoryService.STORE_ALL_INTERVAL)); + constructor( @IFileService fileService: IFileService, @IRemoteAgentService remoteAgentService: IRemoteAgentService, @@ -33,9 +40,17 @@ export class NativeWorkingCopyHistoryService extends WorkingCopyHistoryService { ) { super(fileService, remoteAgentService, environmentService, uriIdentityService, labelService, logService, configurationService); - // When local, delay the flushing until shutdown + this.registerListeners(); + } + + private registerListeners(): void { if (!this.isRemotelyStored) { + + // Local: persist all on shutdown this.lifecycleService.onWillShutdown(e => this.onWillShutdown(e)); + + // Local: schedule persist on change + this._register(Event.any(this.onDidAddEntry, this.onDidChangeEntry, this.onDidReplaceEntry, this.onDidRemoveEntry)(() => this.onDidChangeModels())); } } @@ -45,28 +60,40 @@ export class NativeWorkingCopyHistoryService extends WorkingCopyHistoryService { private onWillShutdown(e: WillShutdownEvent): void { - // Prolong shutdown for orderly model shutdown - e.join((async () => { - const limiter = new Limiter(MAX_PARALLEL_HISTORY_IO_OPS); - const promises = []; + // Dispose the scheduler... + this.storeAllScheduler.dispose(); + this.storeAllCts.dispose(true); - const models = Array.from(this.models.values()); - for (const model of models) { - promises.push(limiter.queue(async () => { - if (e.token.isCancellationRequested) { - return; - } + // ...because we now explicitly store all models + e.join(this.storeAll(e.token), { id: 'join.workingCopyHistory', label: localize('join.workingCopyHistory', "Saving local history") }); + } - try { - await model.store(e.token); - } catch (error) { - this.logService.trace(error); - } - })); - } + private onDidChangeModels(): void { + if (!this.storeAllScheduler.isScheduled()) { + this.storeAllScheduler.schedule(); + } + } - await Promise.all(promises); - })(), { id: 'join.workingCopyHistory', label: localize('join.workingCopyHistory', "Saving local history") }); + private async storeAll(token: CancellationToken): Promise { + const limiter = new Limiter(MAX_PARALLEL_HISTORY_IO_OPS); + const promises = []; + + const models = Array.from(this.models.values()); + for (const model of models) { + promises.push(limiter.queue(async () => { + if (token.isCancellationRequested) { + return; + } + + try { + await model.store(token); + } catch (error) { + this.logService.trace(error); + } + })); + } + + await Promise.all(promises); } } From 697bc2a30d782d550c00f5dc69619e388e07a0bc Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Thu, 14 Apr 2022 08:24:43 -0700 Subject: [PATCH 081/245] Keep composites open when last visible composite is removed (#147309) * fixes #142610 * remove commented code --- src/vs/workbench/browser/parts/compositeBar.ts | 6 ------ src/vs/workbench/browser/parts/panel/panelPart.ts | 8 ++++++++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/browser/parts/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts index 22e337fd491..de6e99067ef 100644 --- a/src/vs/workbench/browser/parts/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -427,12 +427,6 @@ export class CompositeBar extends Widget implements ICompositeBar { this.options.openComposite(defaultCompositeId, true); } - // Case: we closed the last visible composite - // Solv: we hide the part - else if (this.visibleComposites.length <= 1) { - this.options.hidePart(); - } - // Case: we closed the default composite // Solv: we open the next visible composite from top else { diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 3e87cd1f5fd..cc8d9c87d3b 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -370,6 +370,14 @@ export abstract class BasePanelPart extends CompositePart impleme if (viewContainerModel.activeViewDescriptors.length) { contextKey.set(true); this.compositeBar.addComposite({ id: viewContainer.id, name: viewContainer.title, order: viewContainer.order, requestedIndex: viewContainer.requestedIndex }); + + const activeComposite = this.getActiveComposite(); + if (activeComposite === undefined || activeComposite.getId() === viewContainer.id) { + this.compositeBar.activateComposite(viewContainer.id); + } + + this.layoutCompositeBar(); + this.layoutEmptyMessage(); } else if (viewContainer.hideIfEmpty) { contextKey.set(false); this.hideComposite(viewContainer.id); From cb175da74f6f995331a8589ae17f9611153c7c05 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Thu, 14 Apr 2022 09:00:13 -0700 Subject: [PATCH 082/245] try closing editor before running command --- .../codeEditor/browser/untitledTextEditorHint.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint.ts b/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint.ts index 4f2918ddd94..f4fd71ba538 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint.ts @@ -19,6 +19,7 @@ import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { EventType as GestureEventType, Gesture } from 'vs/base/browser/touch'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; const $ = dom.$; @@ -32,6 +33,7 @@ export class UntitledTextEditorHintContribution implements IEditorContribution { constructor( private editor: ICodeEditor, + @IEditorGroupsService private readonly editorGroupsService: IEditorGroupsService, @ICommandService private readonly commandService: ICommandService, @IConfigurationService private readonly configurationService: IConfigurationService, @IKeybindingService private readonly keybindingService: IKeybindingService, @@ -53,7 +55,7 @@ export class UntitledTextEditorHintContribution implements IEditorContribution { const model = this.editor.getModel(); if (model && model.uri.scheme === Schemas.untitled && model.getLanguageId() === PLAINTEXT_LANGUAGE_ID && configValue === 'text') { - this.untitledTextHintContentWidget = new UntitledTextEditorHintContentWidget(this.editor, this.commandService, this.configurationService, this.keybindingService); + this.untitledTextHintContentWidget = new UntitledTextEditorHintContentWidget(this.editor, this.editorGroupsService, this.commandService, this.configurationService, this.keybindingService); } } @@ -72,6 +74,7 @@ class UntitledTextEditorHintContentWidget implements IContentWidget { constructor( private readonly editor: ICodeEditor, + private readonly editorGroupsService: IEditorGroupsService, private readonly commandService: ICommandService, private readonly configurationService: IConfigurationService, private readonly keybindingService: IKeybindingService, @@ -153,6 +156,13 @@ class UntitledTextEditorHintContentWidget implements IContentWidget { const chooseEditorOnClickOrTap = async (e: MouseEvent) => { e.stopPropagation(); + const activeEditorInput = this.editorGroupsService.activeGroup.activeEditor; + + // Close the active editor as long as it is untitled (swap the editors out) + if (activeEditorInput !== null && activeEditorInput.resource?.scheme === Schemas.untitled) { + this.editorGroupsService.activeGroup.closeEditor(activeEditorInput, { preserveFocus: true }); + } + await this.commandService.executeCommand('welcome.showNewFileEntries', { from: 'hint' }); }; this.toDispose.push(dom.addDisposableListener(chooseEditor, 'click', chooseEditorOnClickOrTap)); From 8a58b6585ebd5e2653bf492795ec0f0f246485c8 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Thu, 14 Apr 2022 09:26:57 -0700 Subject: [PATCH 083/245] fixes #147458 --- src/vs/workbench/browser/parts/titlebar/titlebarPart.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index b22c4f246ae..52771febbf5 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -418,7 +418,7 @@ export class TitlebarPart extends Part implements ITitleService { this._register(addDisposableListener(layoutDropdownContainer, EventType.CONTEXT_MENU, e => { EventHelper.stop(e); - this.onLayoutControlContextMenu(e); + this.onLayoutControlContextMenu(e, layoutDropdownContainer); })); @@ -530,7 +530,7 @@ export class TitlebarPart extends Part implements ITitleService { }); } - private onLayoutControlContextMenu(e: MouseEvent): void { + private onLayoutControlContextMenu(e: MouseEvent, el: HTMLElement): void { // Find target anchor const event = new StandardMouseEvent(e); const anchor = { x: event.posx, y: event.posy }; @@ -548,6 +548,7 @@ export class TitlebarPart extends Part implements ITitleService { this.contextMenuService.showContextMenu({ getAnchor: () => anchor, getActions: () => actions, + domForShadowRoot: el }); } From 6c759218a82e65acd4a85e42d802ab5f4cefed53 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 14 Apr 2022 09:03:25 -0800 Subject: [PATCH 084/245] Scope drop into editor to specific editors (#147425) * Scope drop into editor to specific editors As part of #142990, I added support for dropping dragged files from the desktop into an editor by holding shift. To help users discover this feature, we also show a prompt in the editor overlay when dragging However there are a number of editor types that don't actually support drops. The welcome view for example. Readonly editors should also not support dropping. In those cases, it is confusing the show the `hold shift to drop into editor` prompt This change adds a new editor capability (`CanDropIntoEditor`) which signals that the editor does support dropping into the editor. I then enable this new capability on writable files, webviews, and notebooks * Making suggested changes --- .../browser/parts/editor/editorDropTarget.ts | 20 +++++++++++++------ src/vs/workbench/common/editor.ts | 8 +++++++- .../common/editor/resourceEditorInput.ts | 4 ++++ .../customEditor/browser/customEditorInput.ts | 2 ++ .../browser/webviewEditorInput.ts | 2 +- .../common/untitledTextEditorInput.ts | 7 ++++++- 6 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index e3d008f61cc..6315f405d72 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -29,12 +29,12 @@ interface IDropOperation { splitDirection?: GroupDirection; } -function isDropIntoEditorEnabled(configurationService: IConfigurationService) { +function isDropIntoEditorEnabledGlobally(configurationService: IConfigurationService) { return configurationService.getValue('workbench.editor.dropIntoEditor.enabled'); } -function isDragIntoEditorEvent(configurationService: IConfigurationService, e: DragEvent): boolean { - return isDropIntoEditorEnabled(configurationService) && e.shiftKey; +function isDragIntoEditorEvent(e: DragEvent): boolean { + return e.shiftKey; } class DropOverlay extends Themable { @@ -54,6 +54,8 @@ class DropOverlay extends Themable { private readonly groupTransfer = LocalSelectionTransfer.getInstance(); private readonly treeItemsTransfer = LocalSelectionTransfer.getInstance(); + private readonly enableDropIntoEditor: boolean; + constructor( private accessor: IEditorGroupsAccessor, private groupView: IEditorGroupView, @@ -69,6 +71,8 @@ class DropOverlay extends Themable { this.cleanupOverlayScheduler = this._register(new RunOnceScheduler(() => this.dispose(), 300)); + this.enableDropIntoEditor = isDropIntoEditorEnabledGlobally(this.configurationService) && this.isDropIntoActiveEditorEnabled(); + this.create(); } @@ -97,7 +101,7 @@ class DropOverlay extends Themable { this.overlay.classList.add('editor-group-overlay-indicator'); container.appendChild(this.overlay); - if (isDropIntoEditorEnabled(this.configurationService)) { + if (this.enableDropIntoEditor) { this.dropIntoPromptElement = document.createElement('span'); this.dropIntoPromptElement.classList.add('editor-group-overlay-drop-into-prompt'); this.dropIntoPromptElement.textContent = localize('dropIntoEditorPrompt', "Hold shift to drop into editor"); @@ -129,7 +133,7 @@ class DropOverlay extends Themable { this._register(new DragAndDropObserver(container, { onDragEnter: e => undefined, onDragOver: e => { - if (isDragIntoEditorEvent(this.configurationService, e)) { + if (this.enableDropIntoEditor && isDragIntoEditorEvent(e)) { this.dispose(); return; } @@ -209,6 +213,10 @@ class DropOverlay extends Themable { })); } + private isDropIntoActiveEditorEnabled(): boolean { + return !!this.groupView.activeEditor?.hasCapability(EditorInputCapabilities.CanDropIntoEditor); + } + private findSourceGroupView(): IEditorGroupView | undefined { // Check for group transfer @@ -588,7 +596,7 @@ export class EditorDropTarget extends Themable { } private onDragEnter(event: DragEvent): void { - if (isDragIntoEditorEvent(this.configurationService, event)) { + if (isDropIntoEditorEnabledGlobally(this.configurationService) && isDragIntoEditorEvent(event)) { return; } diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index bd610ebd12d..d94d70e015b 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -675,7 +675,13 @@ export const enum EditorInputCapabilities { * component may decide to hide the description portion * for brevity. */ - ForceDescription = 1 << 6 + ForceDescription = 1 << 6, + + /** + * Signals that the editor supports dropping into the + * editor by holding shift. + */ + CanDropIntoEditor = 1 << 7, } export type IUntypedEditorInput = IResourceEditorInput | ITextResourceEditorInput | IUntitledTextResourceEditorInput | IResourceDiffEditorInput | IResourceSideBySideEditorInput; diff --git a/src/vs/workbench/common/editor/resourceEditorInput.ts b/src/vs/workbench/common/editor/resourceEditorInput.ts index 062f9a50439..190a6e63db5 100644 --- a/src/vs/workbench/common/editor/resourceEditorInput.ts +++ b/src/vs/workbench/common/editor/resourceEditorInput.ts @@ -26,6 +26,10 @@ export abstract class AbstractResourceEditorInput extends EditorInput implements capabilities |= EditorInputCapabilities.Untitled; } + if (!(capabilities & EditorInputCapabilities.Readonly)) { + capabilities |= EditorInputCapabilities.CanDropIntoEditor; + } + return capabilities; } diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts index 119a41914f1..81dfae861af 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts @@ -124,6 +124,8 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput { public override get capabilities(): EditorInputCapabilities { let capabilities = EditorInputCapabilities.None; + capabilities |= EditorInputCapabilities.CanDropIntoEditor; + if (!this.customEditorService.getCustomEditorCapabilities(this.viewType)?.supportsMultipleEditorsPerDocument) { capabilities |= EditorInputCapabilities.Singleton; } diff --git a/src/vs/workbench/contrib/webviewPanel/browser/webviewEditorInput.ts b/src/vs/workbench/contrib/webviewPanel/browser/webviewEditorInput.ts index 1caa0e68f25..63e3f1cc124 100644 --- a/src/vs/workbench/contrib/webviewPanel/browser/webviewEditorInput.ts +++ b/src/vs/workbench/contrib/webviewPanel/browser/webviewEditorInput.ts @@ -23,7 +23,7 @@ export class WebviewInput extends EditorInput { } public override get capabilities(): EditorInputCapabilities { - return EditorInputCapabilities.Readonly | EditorInputCapabilities.Singleton; + return EditorInputCapabilities.Readonly | EditorInputCapabilities.Singleton | EditorInputCapabilities.CanDropIntoEditor; } private _name: string; diff --git a/src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts b/src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts index deb041acb8c..e3c3799124f 100644 --- a/src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts +++ b/src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { DEFAULT_EDITOR_ASSOCIATION, findViewStateForEditor, GroupIdentifier, IUntitledTextResourceEditorInput, IUntypedEditorInput, Verbosity } from 'vs/workbench/common/editor'; +import { DEFAULT_EDITOR_ASSOCIATION, EditorInputCapabilities, findViewStateForEditor, GroupIdentifier, IUntitledTextResourceEditorInput, IUntypedEditorInput, Verbosity } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { AbstractTextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput'; import { IUntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel'; @@ -116,6 +116,11 @@ export class UntitledTextEditorInput extends AbstractTextResourceEditorInput imp return this.model.getLanguageId(); } + override get capabilities(): EditorInputCapabilities { + const capabilities = super.capabilities; + return capabilities | EditorInputCapabilities.CanDropIntoEditor; + } + override async resolve(): Promise { if (!this.modelResolve) { this.modelResolve = this.model.resolve(); From 61095a1347029dd6ad913c6565906f21d345f22b Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 14 Apr 2022 10:07:49 -0700 Subject: [PATCH 085/245] notebook controller or id --- src/vs/workbench/api/common/extHost.api.impl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index b5d57211f95..24b09a29eea 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1134,7 +1134,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I extHostApiDeprecation.report('notebookConcatTextDocument', extension, 'This proposal is not on track for finalization and will be removed.'); return new ExtHostNotebookConcatDocument(extHostNotebookDocuments, extHostDocuments, notebook, selector); }, - createNotebookProxyController(id: string, notebookType: string, label: string, handler: () => vscode.NotebookController | Thenable) { + createNotebookProxyController(id: string, notebookType: string, label: string, handler: () => vscode.NotebookController | string | Thenable) { checkProposedApiEnabled(extension, 'notebookProxyController'); return extHostNotebookKernels.createNotebookProxyController(extension, id, notebookType, label, handler); } From f2e6fe3b863d076a36a69b410ad29cf6729cf777 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 14 Apr 2022 10:10:38 -0700 Subject: [PATCH 086/245] Show value on lazy variables after icon. Fix #135147 --- src/vs/workbench/contrib/debug/browser/baseDebugView.ts | 2 +- .../contrib/debug/browser/media/debug.contribution.css | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts index 89f39336b31..e97efddd569 100644 --- a/src/vs/workbench/contrib/debug/browser/baseDebugView.ts +++ b/src/vs/workbench/contrib/debug/browser/baseDebugView.ts @@ -157,9 +157,9 @@ export abstract class AbstractExpressionsRenderer implements ITreeRenderer Date: Thu, 14 Apr 2022 10:48:33 -0700 Subject: [PATCH 087/245] Fixing capabilities for drop into - Copy logic into notebook and file editor since they override `capabilities` from `AbstractResourceEditorInput` - Remove unneeded override --- .../contrib/files/browser/editors/fileEditorInput.ts | 4 ++++ .../contrib/notebook/common/notebookEditorInput.ts | 4 ++++ .../services/untitled/common/untitledTextEditorInput.ts | 7 +------ 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/editors/fileEditorInput.ts b/src/vs/workbench/contrib/files/browser/editors/fileEditorInput.ts index 630cd3a523a..b6317eb2dd7 100644 --- a/src/vs/workbench/contrib/files/browser/editors/fileEditorInput.ts +++ b/src/vs/workbench/contrib/files/browser/editors/fileEditorInput.ts @@ -60,6 +60,10 @@ export class FileEditorInput extends AbstractTextResourceEditorInput implements } } + if (!(capabilities & EditorInputCapabilities.Readonly)) { + capabilities |= EditorInputCapabilities.CanDropIntoEditor; + } + return capabilities; } diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts index cffd2b0362d..6e28efdd088 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts @@ -102,6 +102,10 @@ export class NotebookEditorInput extends AbstractResourceEditorInput { } } + if (!(capabilities & EditorInputCapabilities.Readonly)) { + capabilities |= EditorInputCapabilities.CanDropIntoEditor; + } + return capabilities; } diff --git a/src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts b/src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts index e3c3799124f..deb041acb8c 100644 --- a/src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts +++ b/src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { DEFAULT_EDITOR_ASSOCIATION, EditorInputCapabilities, findViewStateForEditor, GroupIdentifier, IUntitledTextResourceEditorInput, IUntypedEditorInput, Verbosity } from 'vs/workbench/common/editor'; +import { DEFAULT_EDITOR_ASSOCIATION, findViewStateForEditor, GroupIdentifier, IUntitledTextResourceEditorInput, IUntypedEditorInput, Verbosity } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { AbstractTextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput'; import { IUntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel'; @@ -116,11 +116,6 @@ export class UntitledTextEditorInput extends AbstractTextResourceEditorInput imp return this.model.getLanguageId(); } - override get capabilities(): EditorInputCapabilities { - const capabilities = super.capabilities; - return capabilities | EditorInputCapabilities.CanDropIntoEditor; - } - override async resolve(): Promise { if (!this.modelResolve) { this.modelResolve = this.model.resolve(); From 53411d55c5b045d299b531929300c8315812932e Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Thu, 14 Apr 2022 10:58:17 -0700 Subject: [PATCH 088/245] fixes #147468 --- .../services/workspaces/common/workspaceTrust.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/services/workspaces/common/workspaceTrust.ts b/src/vs/workbench/services/workspaces/common/workspaceTrust.ts index 49d6c42d4a8..8a29a82a900 100644 --- a/src/vs/workbench/services/workspaces/common/workspaceTrust.ts +++ b/src/vs/workbench/services/workspaces/common/workspaceTrust.ts @@ -215,22 +215,22 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork this._logService.info('[WT] Enter getCanonicalUri()...'); } + let canonicalUri = uri; if (this.environmentService.remoteAuthority && uri.scheme === Schemas.vscodeRemote) { if (isCI) { this._logService.info('[WT] Return this.remoteAuthorityResolverService.getCanonicalURI(uri)...'); } - return this.remoteAuthorityResolverService.getCanonicalURI(uri); - } - - if (uri.scheme === 'vscode-vfs') { + canonicalUri = await this.remoteAuthorityResolverService.getCanonicalURI(uri); + } else if (uri.scheme === 'vscode-vfs') { const index = uri.authority.indexOf('+'); if (index !== -1) { - return uri.with({ authority: uri.authority.substr(0, index) }); + canonicalUri = uri.with({ authority: uri.authority.substr(0, index) }); } } - return uri; + // ignore query and fragent section of uris always + return canonicalUri.with({ query: null, fragment: null }); } private async resolveCanonicalUris(): Promise { From f20a0f2a10769b0c3eb5da43ba75c12ab44d0d43 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 14 Apr 2022 11:10:48 -0700 Subject: [PATCH 089/245] extract exthost/mainthread proxy kernels. --- .../api/browser/extensionHost.contribution.ts | 1 + .../api/browser/mainThreadNotebookKernels.ts | 91 +--------- .../browser/mainThreadNotebookProxyKernels.ts | 122 ++++++++++++++ .../workbench/api/common/extHost.api.impl.ts | 4 +- .../workbench/api/common/extHost.protocol.ts | 13 +- .../api/common/extHostNotebookKernels.ts | 149 ++--------------- .../api/common/extHostNotebookProxyKernels.ts | 157 ++++++++++++++++++ .../browser/notebookKernelServiceImpl.ts | 28 +--- .../browser/view/cellParts/cellOutput.ts | 9 +- .../notebook/common/notebookKernelService.ts | 4 +- 10 files changed, 316 insertions(+), 262 deletions(-) create mode 100644 src/vs/workbench/api/browser/mainThreadNotebookProxyKernels.ts create mode 100644 src/vs/workbench/api/common/extHostNotebookProxyKernels.ts diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index 37afee52e07..2fa247a959b 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -64,6 +64,7 @@ import './mainThreadWorkspace'; import './mainThreadComments'; import './mainThreadNotebook'; import './mainThreadNotebookKernels'; +import './mainThreadNotebookProxyKernels'; import './mainThreadNotebookDocumentsAndEditors'; import './mainThreadNotebookRenderers'; import './mainThreadInteractive'; diff --git a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts index b94625b1bb6..a8809c58016 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts @@ -15,9 +15,9 @@ import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/ext import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/notebookEditorService'; import { INotebookCellExecution, INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; -import { IResolvedNotebookKernel, INotebookKernelChangeEvent, INotebookKernelService, INotebookProxyKernel, INotebookProxyKernelChangeEvent, ProxyKernelState, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { IResolvedNotebookKernel, INotebookKernelChangeEvent, INotebookKernelService, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { SerializableObjectWithBuffers } from 'vs/workbench/services/extensions/common/proxyIdentifier'; -import { ExtHostContext, ExtHostNotebookKernelsShape, ICellExecuteUpdateDto, ICellExecutionCompleteDto, INotebookKernelDto2, INotebookProxyKernelDto, MainContext, MainThreadNotebookKernelsShape } from '../common/extHost.protocol'; +import { ExtHostContext, ExtHostNotebookKernelsShape, ICellExecuteUpdateDto, ICellExecutionCompleteDto, INotebookKernelDto2, MainContext, MainThreadNotebookKernelsShape } from '../common/extHost.protocol'; abstract class MainThreadKernel implements IResolvedNotebookKernel { readonly type: NotebookKernelType.Resolved = NotebookKernelType.Resolved; @@ -98,58 +98,6 @@ abstract class MainThreadKernel implements IResolvedNotebookKernel { abstract cancelNotebookCellExecution(uri: URI, cellHandles: number[]): Promise; } -abstract class MainThreadProxyKernel implements INotebookProxyKernel { - readonly type: NotebookKernelType.Proxy = NotebookKernelType.Proxy; - protected readonly _onDidChange = new Emitter(); - readonly onDidChange: Event = this._onDidChange.event; - readonly id: string; - readonly viewType: string; - readonly extension: ExtensionIdentifier; - label: string; - description?: string; - detail?: string; - kind?: string; - supportedLanguages: string[] = []; - connectionState: ProxyKernelState; - - constructor(data: INotebookProxyKernelDto) { - this.id = data.id; - this.viewType = data.notebookType; - this.extension = data.extensionId; - - this.label = data.label; - this.description = data.description; - this.detail = data.detail; - this.kind = data.kind; - - this.connectionState = ProxyKernelState.Disconnected; - } - - update(data: Partial) { - const event: INotebookProxyKernelChangeEvent = Object.create(null); - if (data.label !== undefined) { - this.label = data.label; - event.label = true; - } - if (data.description !== undefined) { - this.description = data.description; - event.description = true; - } - if (data.detail !== undefined) { - this.detail = data.detail; - event.detail = true; - } - if (data.kind !== undefined) { - this.kind = data.kind; - event.kind = true; - } - - this._onDidChange.fire(event); - } - - abstract resolveKernel(): Promise; -} - @extHostNamedCustomer(MainContext.MainThreadNotebookKernels) export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape { @@ -157,7 +105,6 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape private readonly _disposables = new DisposableStore(); private readonly _kernels = new Map(); - private readonly _proxyKernels = new Map(); private readonly _proxy: ExtHostNotebookKernelsShape; private readonly _executions = new Map(); @@ -297,40 +244,6 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape } } - // -- Proxy kernel - - async $addProxyKernel(handle: number, data: INotebookProxyKernelDto): Promise { - const that = this; - const proxyKernel = new class extends MainThreadProxyKernel { - async resolveKernel(): Promise { - this.connectionState = ProxyKernelState.Initializing; - this._onDidChange.fire({ connectionState: true }); - const delegateKernel = await that._proxy.$resolveKernel(handle); - this.connectionState = ProxyKernelState.Connected; - this._onDidChange.fire({ connectionState: true }); - return delegateKernel; - } - }(data); - - const listener = this._notebookKernelService.onDidChangeSelectedNotebooks(e => { - if (e.oldKernel === proxyKernel.id) { - this._proxy.$acceptNotebookAssociation(handle, e.notebook, false); - } else if (e.newKernel === proxyKernel.id) { - this._proxy.$acceptNotebookAssociation(handle, e.notebook, true); - } - }); - - const registration = this._notebookKernelService.registerProxyKernel(proxyKernel); - this._proxyKernels.set(handle, [proxyKernel, combinedDisposable(listener, registration)]); - } - - $updateProxyKernel(handle: number, data: Partial): void { - const tuple = this._proxyKernels.get(handle); - if (tuple) { - tuple[0].update(data); - } - } - // --- execution $createExecution(handle: number, controllerId: string, rawUri: UriComponents, cellHandle: number): void { diff --git a/src/vs/workbench/api/browser/mainThreadNotebookProxyKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookProxyKernels.ts new file mode 100644 index 00000000000..3db46989937 --- /dev/null +++ b/src/vs/workbench/api/browser/mainThreadNotebookProxyKernels.ts @@ -0,0 +1,122 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter, Event } from 'vs/base/common/event'; +import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; +import { INotebookKernelService, INotebookProxyKernel, INotebookProxyKernelChangeEvent, ProxyKernelState, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { ExtHostContext, ExtHostNotebookProxyKernelsShape, INotebookProxyKernelDto, MainContext, MainThreadNotebookProxyKernelsShape } from '../common/extHost.protocol'; + +abstract class MainThreadProxyKernel implements INotebookProxyKernel { + readonly type: NotebookKernelType.Proxy = NotebookKernelType.Proxy; + protected readonly _onDidChange = new Emitter(); + readonly onDidChange: Event = this._onDidChange.event; + readonly id: string; + readonly viewType: string; + readonly extension: ExtensionIdentifier; + readonly preloadProvides: string[] = []; + label: string; + description?: string; + detail?: string; + kind?: string; + supportedLanguages: string[] = []; + connectionState: ProxyKernelState; + + constructor(data: INotebookProxyKernelDto) { + this.id = data.id; + this.viewType = data.notebookType; + this.extension = data.extensionId; + + this.label = data.label; + this.description = data.description; + this.detail = data.detail; + this.kind = data.kind; + + this.connectionState = ProxyKernelState.Disconnected; + } + + update(data: Partial) { + const event: INotebookProxyKernelChangeEvent = Object.create(null); + if (data.label !== undefined) { + this.label = data.label; + event.label = true; + } + if (data.description !== undefined) { + this.description = data.description; + event.description = true; + } + if (data.detail !== undefined) { + this.detail = data.detail; + event.detail = true; + } + if (data.kind !== undefined) { + this.kind = data.kind; + event.kind = true; + } + + this._onDidChange.fire(event); + } + + abstract resolveKernel(): Promise; +} + +@extHostNamedCustomer(MainContext.MainThreadNotebookProxyKernels) +export class MainThreadNotebookProxyKernels implements MainThreadNotebookProxyKernelsShape { + + private readonly _disposables = new DisposableStore(); + + private readonly _proxyKernels = new Map(); + private readonly _proxyKernelProxy: ExtHostNotebookProxyKernelsShape; + + constructor( + extHostContext: IExtHostContext, + @INotebookKernelService private readonly _notebookKernelService: INotebookKernelService, + ) { + this._proxyKernelProxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebookProxyKernels); + } + + dispose(): void { + this._disposables.dispose(); + + for (let [, registration] of this._proxyKernels.values()) { + registration.dispose(); + } + } + + // -- Proxy kernel + + async $addProxyKernel(handle: number, data: INotebookProxyKernelDto): Promise { + const that = this; + const proxyKernel = new class extends MainThreadProxyKernel { + async resolveKernel(): Promise { + this.connectionState = ProxyKernelState.Initializing; + this._onDidChange.fire({ connectionState: true }); + const delegateKernel = await that._proxyKernelProxy.$resolveKernel(handle); + this.connectionState = ProxyKernelState.Connected; + this._onDidChange.fire({ connectionState: true }); + return delegateKernel; + } + }(data); + + const registration = this._notebookKernelService.registerKernel(proxyKernel); + this._proxyKernels.set(handle, [proxyKernel, registration]); + } + + $updateProxyKernel(handle: number, data: Partial): void { + const tuple = this._proxyKernels.get(handle); + if (tuple) { + tuple[0].update(data); + } + } + + $removeProxyKernel(handle: number): void { + const tuple = this._proxyKernels.get(handle); + if (tuple) { + tuple[1].dispose(); + this._proxyKernels.delete(handle); + } + } +} diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index d141f26ab57..668f8eb02c6 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -94,6 +94,7 @@ import { ExtHostInteractive } from 'vs/workbench/api/common/extHostInteractive'; import { combinedDisposable } from 'vs/base/common/lifecycle'; import { checkProposedApiEnabled, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/contrib/debug/common/debug'; +import { ExtHostNotebookProxyKernels } from 'vs/workbench/api/common/extHostNotebookProxyKernels'; export interface IExtensionApiFactory { (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; @@ -156,6 +157,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostNotebookDocuments = rpcProtocol.set(ExtHostContext.ExtHostNotebookDocuments, new ExtHostNotebookDocuments(extHostNotebook)); const extHostNotebookEditors = rpcProtocol.set(ExtHostContext.ExtHostNotebookEditors, new ExtHostNotebookEditors(extHostLogService, rpcProtocol, extHostNotebook)); const extHostNotebookKernels = rpcProtocol.set(ExtHostContext.ExtHostNotebookKernels, new ExtHostNotebookKernels(rpcProtocol, initData, extHostNotebook, extHostCommands, extHostLogService)); + const extHostNotebookProxyKernels = rpcProtocol.set(ExtHostContext.ExtHostNotebookProxyKernels, new ExtHostNotebookProxyKernels(rpcProtocol, extHostNotebookKernels, extHostLogService)); const extHostNotebookRenderers = rpcProtocol.set(ExtHostContext.ExtHostNotebookRenderers, new ExtHostNotebookRenderers(rpcProtocol, extHostNotebook)); const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors)); const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService)); @@ -1135,7 +1137,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }, createNotebookProxyController(id: string, notebookType: string, label: string, handler: () => vscode.NotebookController | string | Thenable) { checkProposedApiEnabled(extension, 'notebookProxyController'); - return extHostNotebookKernels.createNotebookProxyController(extension, id, notebookType, label, handler); + return extHostNotebookProxyKernels.createNotebookProxyController(extension, id, notebookType, label, handler); } }; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 966d6577246..8ccdce38e97 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1018,8 +1018,6 @@ export interface MainThreadNotebookKernelsShape extends IDisposable { $postMessage(handle: number, editorId: string | undefined, message: any): Promise; $addKernel(handle: number, data: INotebookKernelDto2): Promise; $updateKernel(handle: number, data: Partial): void; - $addProxyKernel(handle: number, data: INotebookProxyKernelDto): Promise; - $updateProxyKernel(handle: number, data: Partial): void; $removeKernel(handle: number): void; $updateNotebookPriority(handle: number, uri: UriComponents, value: number | undefined): void; @@ -1028,6 +1026,12 @@ export interface MainThreadNotebookKernelsShape extends IDisposable { $completeExecution(handle: number, data: SerializableObjectWithBuffers): void; } +export interface MainThreadNotebookProxyKernelsShape extends IDisposable { + $addProxyKernel(handle: number, data: INotebookProxyKernelDto): Promise; + $updateProxyKernel(handle: number, data: Partial): void; + $removeProxyKernel(handle: number): void; +} + export interface MainThreadNotebookRenderersShape extends IDisposable { $postMessage(editorId: string | undefined, rendererId: string, message: unknown): Promise; } @@ -2114,6 +2118,9 @@ export interface ExtHostNotebookKernelsShape { $cancelCells(handle: number, uri: UriComponents, handles: number[]): Promise; $acceptKernelMessageFromRenderer(handle: number, editorId: string, message: any): void; $cellExecutionChanged(uri: UriComponents, cellHandle: number, state: notebookCommon.NotebookCellExecutionState | undefined): void; +} + +export interface ExtHostNotebookProxyKernelsShape { $resolveKernel(handle: number): Promise; } @@ -2293,6 +2300,7 @@ export const MainContext = { MainThreadNotebookDocuments: createProxyIdentifier('MainThreadNotebookDocumentsShape'), MainThreadNotebookEditors: createProxyIdentifier('MainThreadNotebookEditorsShape'), MainThreadNotebookKernels: createProxyIdentifier('MainThreadNotebookKernels'), + MainThreadNotebookProxyKernels: createProxyIdentifier('MainThreadNotebookProxyKernels'), MainThreadNotebookRenderers: createProxyIdentifier('MainThreadNotebookRenderers'), MainThreadInteractive: createProxyIdentifier('MainThreadInteractive'), MainThreadTheming: createProxyIdentifier('MainThreadTheming'), @@ -2345,6 +2353,7 @@ export const ExtHostContext = { ExtHostNotebookDocuments: createProxyIdentifier('ExtHostNotebookDocuments'), ExtHostNotebookEditors: createProxyIdentifier('ExtHostNotebookEditors'), ExtHostNotebookKernels: createProxyIdentifier('ExtHostNotebookKernels'), + ExtHostNotebookProxyKernels: createProxyIdentifier('ExtHostNotebookProxyKernels'), ExtHostNotebookRenderers: createProxyIdentifier('ExtHostNotebookRenderers'), ExtHostInteractive: createProxyIdentifier('ExtHostInteractive'), ExtHostTheming: createProxyIdentifier('ExtHostTheming'), diff --git a/src/vs/workbench/api/common/extHostNotebookKernels.ts b/src/vs/workbench/api/common/extHostNotebookKernels.ts index b3d560db5f5..2ec0c9552c3 100644 --- a/src/vs/workbench/api/common/extHostNotebookKernels.ts +++ b/src/vs/workbench/api/common/extHostNotebookKernels.ts @@ -12,7 +12,7 @@ import { ResourceMap } from 'vs/base/common/map'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; -import { ExtHostNotebookKernelsShape, ICellExecuteUpdateDto, IMainContext, INotebookKernelDto2, INotebookProxyKernelDto, MainContext, MainThreadNotebookKernelsShape, NotebookOutputDto } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostNotebookKernelsShape, ICellExecuteUpdateDto, IMainContext, INotebookKernelDto2, MainContext, MainThreadNotebookKernelsShape, NotebookOutputDto } from 'vs/workbench/api/common/extHost.protocol'; import { ApiCommand, ApiCommandArgument, ApiCommandResult, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; @@ -34,13 +34,6 @@ interface IKernelData { associatedNotebooks: ResourceMap; } -interface IProxyKernelData { - extensionId: ExtensionIdentifier; - controller: vscode.NotebookProxyController; - onDidChangeSelection: Emitter<{ selected: boolean; notebook: vscode.NotebookDocument }>; - associatedNotebooks: ResourceMap; -} - type ExtHostSelectKernelArgs = ControllerInfo | { notebookEditor: vscode.NotebookEditor } | ControllerInfo & { notebookEditor: vscode.NotebookEditor } | undefined; export type SelectKernelReturnArgs = ControllerInfo | { notebookEditorId: string } | ControllerInfo & { notebookEditorId: string } | undefined; type ControllerInfo = { id: string; extension: string }; @@ -52,7 +45,6 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { private readonly _activeExecutions = new ResourceMap(); private readonly _kernelData = new Map(); - private readonly _proxyKernelData: Map = new Map(); private _handlePool: number = 0; private readonly _onDidChangeCellExecutionState = new Emitter(); @@ -116,7 +108,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { const onDidReceiveMessage = new Emitter<{ editor: vscode.NotebookEditor; message: any }>(); const data: INotebookKernelDto2 = { - id: createKernelId(extension, id), + id: createKernelId(extension.identifier, id), notebookType: viewType, extensionId: extension.identifier, extensionLocation: extension.extensionLocation, @@ -226,7 +218,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { that._logService.trace(`NotebookController[${handle}] NOT associated to notebook, associated to THESE notebooks:`, Array.from(associatedNotebooks.keys()).map(u => u.toString())); throw new Error(`notebook controller is NOT associated to notebook: ${cell.notebook.uri.toString()}`); } - return that._createNotebookCellExecution(cell, createKernelId(extension, this.id)); + return that._createNotebookCellExecution(cell, createKernelId(extension.identifier, this.id)); }, dispose: () => { if (!isDisposed) { @@ -265,105 +257,18 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { return controller; } - createNotebookProxyController(extension: IExtensionDescription, id: string, viewType: string, label: string, handler: () => vscode.NotebookController | string | Thenable): vscode.NotebookProxyController { - const handle = this._handlePool++; - - let isDisposed = false; - const commandDisposables = new DisposableStore(); - const onDidChangeSelection = new Emitter<{ selected: boolean; notebook: vscode.NotebookDocument }>(); - - const data: INotebookProxyKernelDto = { - id: createKernelId(extension, id), - notebookType: viewType, - extensionId: extension.identifier, - extensionLocation: extension.extensionLocation, - label: label || extension.identifier.value, - }; - - let _resolveHandler = handler; - - this._proxy.$addProxyKernel(handle, data).catch(err => { - // this can happen when a kernel with that ID is already registered - console.log(err); - isDisposed = true; - }); - - let tokenPool = 0; - const _update = () => { - if (isDisposed) { - return; + getIdByController(controller: vscode.NotebookController) { + // return this._kernelData.(data => data.controller === controller); + for (const [_, candidate] of this._kernelData) { + if (candidate.controller === controller) { + return createKernelId(candidate.extensionId, controller.id); } - const myToken = ++tokenPool; - Promise.resolve().then(() => { - if (myToken === tokenPool) { - this._proxy.$updateProxyKernel(handle, data); - } - }); - }; - - // notebook documents that are associated to this controller - const associatedNotebooks = new ResourceMap(); - - const controller: vscode.NotebookProxyController = { - get id() { return id; }, - get notebookType() { return data.notebookType; }, - onDidChangeSelectedNotebooks: onDidChangeSelection.event, - get label() { - return data.label; - }, - set label(value) { - data.label = value ?? extension.displayName ?? extension.name; - _update(); - }, - get detail() { - return data.detail ?? ''; - }, - set detail(value) { - data.detail = value; - _update(); - }, - get description() { - return data.description ?? ''; - }, - set description(value) { - data.description = value; - _update(); - }, - get kind() { - checkProposedApiEnabled(extension, 'notebookControllerKind'); - return data.kind ?? ''; - }, - set kind(value) { - checkProposedApiEnabled(extension, 'notebookControllerKind'); - data.kind = value; - _update(); - }, - get resolveHandler() { - return _resolveHandler; - }, - dispose: () => { - if (!isDisposed) { - this._logService.trace(`NotebookController[${handle}], DISPOSED`); - isDisposed = true; - this._kernelData.delete(handle); - commandDisposables.dispose(); - onDidChangeSelection.dispose(); - this._proxy.$removeKernel(handle); - } - } - }; - - this._proxyKernelData.set(handle, { - extensionId: extension.identifier, - controller, - onDidChangeSelection, - associatedNotebooks - }); - return controller; + } + return null; } $acceptNotebookAssociation(handle: number, uri: UriComponents, value: boolean): void { - const obj = this._kernelData.get(handle) ?? this._proxyKernelData.get(handle); + const obj = this._kernelData.get(handle); if (obj) { // update data structure const notebook = this._extHostNotebook.getNotebookDocument(URI.revive(uri))!; @@ -451,34 +356,6 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { } } - async $resolveKernel(handle: number): Promise { - const obj = this._proxyKernelData.get(handle); - if (!obj) { - // extension can dispose kernels in the meantime - return null; - } - - const controller = await obj.controller.resolveHandler(); - let matchedKernelData: IKernelData | undefined; - this._kernelData.forEach(d => { - if (typeof controller === 'string') { - if (d.controller.id === controller) { - matchedKernelData = d; - } - } else { - if (d.controller.id === controller.id) { - matchedKernelData = d; - } - } - }); - - if (matchedKernelData) { - return `${matchedKernelData.extensionId.value}/${matchedKernelData.controller.id}`; - } - - return null; - } - // --- _createNotebookCellExecution(cell: vscode.NotebookCell, controllerId: string): vscode.NotebookCellExecution { @@ -716,6 +593,6 @@ class TimeoutBasedCollector { } } -function createKernelId(extension: IExtensionDescription, id: string): string { - return `${extension.identifier.value}/${id}`; +export function createKernelId(extensionIdentifier: ExtensionIdentifier, id: string): string { + return `${extensionIdentifier.value}/${id}`; } diff --git a/src/vs/workbench/api/common/extHostNotebookProxyKernels.ts b/src/vs/workbench/api/common/extHostNotebookProxyKernels.ts new file mode 100644 index 00000000000..786101bf360 --- /dev/null +++ b/src/vs/workbench/api/common/extHostNotebookProxyKernels.ts @@ -0,0 +1,157 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { DisposableStore } from 'vs/base/common/lifecycle'; +import { ResourceMap } from 'vs/base/common/map'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ILogService } from 'vs/platform/log/common/log'; +import { ExtHostNotebookProxyKernelsShape, IMainContext, INotebookProxyKernelDto, MainContext, MainThreadNotebookProxyKernelsShape } from 'vs/workbench/api/common/extHost.protocol'; +import { createKernelId, ExtHostNotebookKernels } from 'vs/workbench/api/common/extHostNotebookKernels'; +import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; +import * as vscode from 'vscode'; + +interface IProxyKernelData { + extensionId: ExtensionIdentifier; + controller: vscode.NotebookProxyController; + onDidChangeSelection: Emitter<{ selected: boolean; notebook: vscode.NotebookDocument }>; + associatedNotebooks: ResourceMap; +} + +export type SelectKernelReturnArgs = ControllerInfo | { notebookEditorId: string } | ControllerInfo & { notebookEditorId: string } | undefined; +type ControllerInfo = { id: string; extension: string }; + + +export class ExtHostNotebookProxyKernels implements ExtHostNotebookProxyKernelsShape { + + private readonly _proxy: MainThreadNotebookProxyKernelsShape; + + private readonly _proxyKernelData: Map = new Map(); + private _handlePool: number = 0; + + private readonly _onDidChangeCellExecutionState = new Emitter(); + readonly onDidChangeNotebookCellExecutionState = this._onDidChangeCellExecutionState.event; + + constructor( + mainContext: IMainContext, + private readonly extHostNotebook: ExtHostNotebookKernels, + @ILogService private readonly _logService: ILogService + ) { + this._proxy = mainContext.getProxy(MainContext.MainThreadNotebookProxyKernels); + } + + createNotebookProxyController(extension: IExtensionDescription, id: string, viewType: string, label: string, handler: () => vscode.NotebookController | string | Thenable): vscode.NotebookProxyController { + const handle = this._handlePool++; + + let isDisposed = false; + const commandDisposables = new DisposableStore(); + const onDidChangeSelection = new Emitter<{ selected: boolean; notebook: vscode.NotebookDocument }>(); + + const data: INotebookProxyKernelDto = { + id: createKernelId(extension.identifier, id), + notebookType: viewType, + extensionId: extension.identifier, + extensionLocation: extension.extensionLocation, + label: label || extension.identifier.value, + }; + + let _resolveHandler = handler; + + this._proxy.$addProxyKernel(handle, data).catch(err => { + // this can happen when a kernel with that ID is already registered + console.log(err); + isDisposed = true; + }); + + let tokenPool = 0; + const _update = () => { + if (isDisposed) { + return; + } + const myToken = ++tokenPool; + Promise.resolve().then(() => { + if (myToken === tokenPool) { + this._proxy.$updateProxyKernel(handle, data); + } + }); + }; + + // notebook documents that are associated to this controller + const associatedNotebooks = new ResourceMap(); + + const controller: vscode.NotebookProxyController = { + get id() { return id; }, + get notebookType() { return data.notebookType; }, + onDidChangeSelectedNotebooks: onDidChangeSelection.event, + get label() { + return data.label; + }, + set label(value) { + data.label = value ?? extension.displayName ?? extension.name; + _update(); + }, + get detail() { + return data.detail ?? ''; + }, + set detail(value) { + data.detail = value; + _update(); + }, + get description() { + return data.description ?? ''; + }, + set description(value) { + data.description = value; + _update(); + }, + get kind() { + checkProposedApiEnabled(extension, 'notebookControllerKind'); + return data.kind ?? ''; + }, + set kind(value) { + checkProposedApiEnabled(extension, 'notebookControllerKind'); + data.kind = value; + _update(); + }, + get resolveHandler() { + return _resolveHandler; + }, + dispose: () => { + if (!isDisposed) { + this._logService.trace(`NotebookProxyController[${handle}], DISPOSED`); + isDisposed = true; + this._proxyKernelData.delete(handle); + commandDisposables.dispose(); + onDidChangeSelection.dispose(); + this._proxy.$removeProxyKernel(handle); + } + } + }; + + this._proxyKernelData.set(handle, { + extensionId: extension.identifier, + controller, + onDidChangeSelection, + associatedNotebooks + }); + return controller; + } + + async $resolveKernel(handle: number): Promise { + const obj = this._proxyKernelData.get(handle); + if (!obj) { + // extension can dispose kernels in the meantime + return null; + } + + const controller = await obj.controller.resolveHandler(); + if (typeof controller === 'string') { + return controller; + } else { + return this.extHostNotebook.getIdByController(controller); + } + } +} + diff --git a/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts index 09e75953d84..1f2e2e72032 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookKernelServiceImpl.ts @@ -6,7 +6,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { INotebookTextModel } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { INotebookKernel, ISelectedNotebooksChangeEvent, INotebookKernelMatchResult, INotebookKernelService, INotebookTextModelLike, INotebookProxyKernel } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, ISelectedNotebooksChangeEvent, INotebookKernelMatchResult, INotebookKernelService, INotebookTextModelLike } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { LRUCache, ResourceMap } from 'vs/base/common/map'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { URI } from 'vs/base/common/uri'; @@ -164,32 +164,6 @@ export class NotebookKernelService extends Disposable implements INotebookKernel }); } - registerProxyKernel(kernel: INotebookProxyKernel): IDisposable { - if (this._kernels.has(kernel.id)) { - throw new Error(`NOTEBOOK CONTROLLER with id '${kernel.id}' already exists`); - } - - this._kernels.set(kernel.id, new KernelInfo(kernel)); - this._onDidAddKernel.fire(kernel); - - // auto associate the new kernel to existing notebooks it was - // associated to in the past. - for (const notebook of this._notebookService.getNotebookTextModels()) { - this._tryAutoBindNotebook(notebook, kernel); - } - - return toDisposable(() => { - if (this._kernels.delete(kernel.id)) { - this._onDidRemoveKernel.fire(kernel); - } - for (const [key, candidate] of Array.from(this._notebookBindings)) { - if (candidate === kernel.id) { - this._onDidChangeNotebookKernelBinding.fire({ notebook: NotebookTextModelLikeId.obj(key).uri, oldKernel: kernel.id, newKernel: undefined }); - } - } - }); - } - getMatchingKernel(notebook: INotebookTextModelLike): INotebookKernelMatchResult { // all applicable kernels 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 7ed12d9bb09..7306836ac30 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts @@ -31,7 +31,7 @@ import { CodeCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/vi import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { CellUri, IOrderedMimeType, NotebookCellOutputsSplice, RENDERER_NOT_AVAILABLE } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { INotebookKernel, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; @@ -127,8 +127,7 @@ export class CellOutputElement extends Disposable { this.notebookEditor.hasModel() && this.innerContainer && this.renderResult && - this.renderResult.type === RenderOutputType.Extension && - this.notebookEditor.activeKernel?.type === NotebookKernelType.Resolved + this.renderResult.type === RenderOutputType.Extension ) { // Output rendered by extension renderer got an update const [mimeTypes, pick] = this.output.resolveMimeTypes(this.notebookEditor.textModel, this.notebookEditor.activeKernel?.preloadProvides); @@ -192,7 +191,7 @@ export class CellOutputElement extends Disposable { const notebookTextModel = this.notebookEditor.textModel; - const [mimeTypes, pick] = this.output.resolveMimeTypes(notebookTextModel, this.notebookEditor.activeKernel?.type === NotebookKernelType.Resolved ? this.notebookEditor.activeKernel?.preloadProvides : undefined); + const [mimeTypes, pick] = this.output.resolveMimeTypes(notebookTextModel, this.notebookEditor.activeKernel?.preloadProvides); if (!mimeTypes.find(mimeType => mimeType.isTrusted) || mimeTypes.length === 0) { this.viewCell.updateOutputHeight(index, 0, 'CellOutputElement#noMimeType'); @@ -300,7 +299,7 @@ export class CellOutputElement extends Disposable { } private async _pickActiveMimeTypeRenderer(outputItemDiv: HTMLElement, notebookTextModel: NotebookTextModel, kernel: INotebookKernel | undefined, viewModel: ICellOutputViewModel) { - const [mimeTypes, currIndex] = viewModel.resolveMimeTypes(notebookTextModel, kernel?.type === NotebookKernelType.Resolved ? kernel?.preloadProvides : undefined); + const [mimeTypes, currIndex] = viewModel.resolveMimeTypes(notebookTextModel, kernel?.preloadProvides); const items: IMimeTypeRenderer[] = []; const unsupportedItems: IMimeTypeRenderer[] = []; diff --git a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts index 19ee2cf2b66..4f5304600b0 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookKernelService.ts @@ -74,6 +74,7 @@ export interface INotebookProxyKernel { readonly id: string; readonly viewType: string; readonly extension: ExtensionIdentifier; + readonly preloadProvides: string[]; readonly onDidChange: Event>; label: string; description?: string; @@ -98,8 +99,7 @@ export interface INotebookKernelService { readonly onDidChangeSelectedNotebooks: Event; readonly onDidChangeNotebookAffinity: Event; - registerKernel(kernel: IResolvedNotebookKernel): IDisposable; - registerProxyKernel(proxyKernel: INotebookProxyKernel): IDisposable; + registerKernel(kernel: INotebookKernel): IDisposable; getMatchingKernel(notebook: INotebookTextModelLike): INotebookKernelMatchResult; From 280050dba576740f0350245409d147aa2c84aa94 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 14 Apr 2022 11:19:50 -0700 Subject: [PATCH 090/245] :lipstick: --- src/vs/workbench/api/common/extHostNotebookKernels.ts | 1 - .../contrib/notebook/browser/notebookEditorWidget.ts | 10 +--------- .../browser/viewParts/notebookKernelActionViewItem.ts | 6 +++--- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/api/common/extHostNotebookKernels.ts b/src/vs/workbench/api/common/extHostNotebookKernels.ts index 2ec0c9552c3..f99ead59ac7 100644 --- a/src/vs/workbench/api/common/extHostNotebookKernels.ts +++ b/src/vs/workbench/api/common/extHostNotebookKernels.ts @@ -258,7 +258,6 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { } getIdByController(controller: vscode.NotebookController) { - // return this._kernelData.(data => data.controller === controller); for (const [_, candidate] of this._kernelData) { if (candidate.controller === controller) { return createKernelId(candidate.extensionId, controller.id); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 96912b1f10c..40712d1eaa3 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -76,7 +76,7 @@ import { CellKind, INotebookSearchOptions, SelectionStateType } from 'vs/workben import { NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_OUTPUT_FOCUSED } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import { INotebookExecutionService } from 'vs/workbench/contrib/notebook/common/notebookExecutionService'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; -import { INotebookKernelService, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { NotebookOptions, OutputInnerContainerTopPadding } from 'vs/workbench/contrib/notebook/common/notebookOptions'; import { mark } from 'vs/workbench/contrib/notebook/common/notebookPerformance'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; @@ -2112,14 +2112,6 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return; } const { selected } = this.notebookKernelService.getMatchingKernel(this.textModel); - if (!selected) { - return; - } - - if (selected.type === NotebookKernelType.Proxy) { - return; - } - if (!this._webview?.isResolved()) { await this._resolveWebview(); } diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts index 41e1ec74773..a4060aaa62e 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts @@ -6,14 +6,14 @@ import 'vs/css!./notebookKernelActionViewItem'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { Action, IAction } from 'vs/base/common/actions'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { selectKernelIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; -import { INotebookKernelMatchResult, INotebookKernelService, ProxyKernelState } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernelMatchResult, INotebookKernelService, NotebookKernelType, ProxyKernelState } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { Event } from 'vs/base/common/event'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { DisposableStore } from 'vs/base/common/lifecycle'; export class NotebooKernelActionViewItem extends ActionViewItem { @@ -77,7 +77,7 @@ export class NotebooKernelActionViewItem extends ActionViewItem { // special UI for selected kernel? } - if ('resolveKernel' in selectedOrSuggested) { + if (selectedOrSuggested.type === NotebookKernelType.Proxy) { if (selectedOrSuggested.connectionState === ProxyKernelState.Initializing) { this._action.label = localize('initializing', "Initializing..."); } else { From 3c9c0e36cd536805b8d376d1f1e09b6db0c53522 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 6 Apr 2022 10:39:47 -0700 Subject: [PATCH 091/245] Use SELECT_KERNEL_ID other than string literal --- .../browser/contrib/editorStatusBar/editorStatusBar.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts index cb7ab0f8586..83b35ce916d 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts @@ -38,7 +38,7 @@ import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeat registerAction2(class extends Action2 { constructor() { super({ - id: '_notebook.selectKernel', + id: SELECT_KERNEL_ID, category: NOTEBOOK_ACTIONS_CATEGORY, title: { value: nls.localize('notebookActions.selectKernel', "Select Notebook Kernel"), original: 'Select Notebook Kernel' }, // precondition: NOTEBOOK_IS_ACTIVE_EDITOR, @@ -381,7 +381,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution { tooltip: isSuggested ? nls.localize('tooltop', "{0} (suggestion)", tooltip) : tooltip, command: SELECT_KERNEL_ID, }, - '_notebook.selectKernel', + SELECT_KERNEL_ID, StatusbarAlignment.RIGHT, 10 )); @@ -399,7 +399,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution { command: SELECT_KERNEL_ID, backgroundColor: { id: 'statusBarItem.prominentBackground' } }, - '_notebook.selectKernel', + SELECT_KERNEL_ID, StatusbarAlignment.RIGHT, 10 )); From f6985089f4a54d97c86eb180277f273eb632769f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 14 Apr 2022 11:21:54 -0700 Subject: [PATCH 092/245] Improving UX for drop into prompt For #142990 --- .../browser/parts/editor/editorDropTarget.ts | 52 ++++++++++++------- .../parts/editor/media/editordroptarget.css | 2 +- src/vs/workbench/common/theme.ts | 21 ++++++++ 3 files changed, 55 insertions(+), 20 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index 6315f405d72..d6bfb2da22e 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -3,27 +3,28 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { DataTransfers } from 'vs/base/browser/dnd'; +import { addDisposableListener, DragAndDropObserver, EventHelper, EventType, isAncestor } from 'vs/base/browser/dom'; +import { renderFormattedText } from 'vs/base/browser/formattedTextRenderer'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { toDisposable } from 'vs/base/common/lifecycle'; +import { isMacintosh, isWeb } from 'vs/base/common/platform'; +import { assertAllDefined, assertIsDefined } from 'vs/base/common/types'; import 'vs/css!./media/editordroptarget'; import { localize } from 'vs/nls'; -import { Extensions as DragAndDropExtensions, LocalSelectionTransfer, DraggedEditorIdentifier, ResourcesDropHandler, DraggedEditorGroupIdentifier, containsDragType, CodeDataTransfers, DraggedTreeItemsIdentifier, extractTreeDropData, IDragAndDropContributionRegistry } from 'vs/workbench/browser/dnd'; -import { addDisposableListener, EventType, EventHelper, isAncestor, DragAndDropObserver } from 'vs/base/browser/dom'; -import { IEditorGroupsAccessor, IEditorGroupView, fillActiveEditorViewState } from 'vs/workbench/browser/parts/editor/editor'; -import { EDITOR_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme'; -import { IThemeService, Themable } from 'vs/platform/theme/common/themeService'; -import { activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; -import { IEditorIdentifier, EditorInputCapabilities, IUntypedEditorInput } from 'vs/workbench/common/editor'; -import { isMacintosh, isWeb } from 'vs/base/common/platform'; -import { GroupDirection, IEditorGroupsService, IMergeGroupOptions, MergeGroupMode } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { toDisposable } from 'vs/base/common/lifecycle'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { RunOnceScheduler } from 'vs/base/common/async'; -import { DataTransfers } from 'vs/base/browser/dnd'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { assertIsDefined, assertAllDefined } from 'vs/base/common/types'; -import { ITreeViewsService } from 'vs/workbench/services/views/browser/treeViewsService'; -import { isTemporaryWorkspace, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Registry } from 'vs/platform/registry/common/platform'; +import { activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; +import { IThemeService, Themable } from 'vs/platform/theme/common/themeService'; +import { isTemporaryWorkspace, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { CodeDataTransfers, containsDragType, DraggedEditorGroupIdentifier, DraggedEditorIdentifier, DraggedTreeItemsIdentifier, Extensions as DragAndDropExtensions, extractTreeDropData, IDragAndDropContributionRegistry, LocalSelectionTransfer, ResourcesDropHandler } from 'vs/workbench/browser/dnd'; +import { fillActiveEditorViewState, IEditorGroupsAccessor, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor'; +import { EditorInputCapabilities, IEditorIdentifier, IUntypedEditorInput } from 'vs/workbench/common/editor'; +import { EDITOR_DRAG_AND_DROP_BACKGROUND, EDITOR_DROP_INTO_PROMPT_BACKGROUND, EDITOR_DROP_INTO_PROMPT_BORDER, EDITOR_DROP_INTO_PROMPT_FOREGROUND } from 'vs/workbench/common/theme'; +import { GroupDirection, IEditorGroupsService, IMergeGroupOptions, MergeGroupMode } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ITreeViewsService } from 'vs/workbench/services/views/browser/treeViewsService'; interface IDropOperation { splitDirection?: GroupDirection; @@ -102,9 +103,8 @@ class DropOverlay extends Themable { container.appendChild(this.overlay); if (this.enableDropIntoEditor) { - this.dropIntoPromptElement = document.createElement('span'); + this.dropIntoPromptElement = renderFormattedText(localize('dropIntoEditorPrompt', "Hold __shift__ to drop into editor"), {}); this.dropIntoPromptElement.classList.add('editor-group-overlay-drop-into-prompt'); - this.dropIntoPromptElement.textContent = localize('dropIntoEditorPrompt', "Hold shift to drop into editor"); this.overlay.appendChild(this.dropIntoPromptElement); } @@ -127,6 +127,20 @@ class DropOverlay extends Themable { overlay.style.outlineOffset = activeContrastBorderColor ? '-2px' : ''; overlay.style.outlineStyle = activeContrastBorderColor ? 'dashed' : ''; overlay.style.outlineWidth = activeContrastBorderColor ? '2px' : ''; + + if (this.dropIntoPromptElement) { + this.dropIntoPromptElement.style.backgroundColor = this.getColor(EDITOR_DROP_INTO_PROMPT_BACKGROUND) ?? ''; + this.dropIntoPromptElement.style.color = this.getColor(EDITOR_DROP_INTO_PROMPT_FOREGROUND) ?? ''; + + const borderColor = this.getColor(EDITOR_DROP_INTO_PROMPT_BORDER); + if (borderColor) { + this.dropIntoPromptElement.style.borderWidth = '1px'; + this.dropIntoPromptElement.style.borderStyle = 'solid'; + this.dropIntoPromptElement.style.borderColor = borderColor; + } else { + this.dropIntoPromptElement.style.borderWidth = '0'; + } + } } private registerListeners(container: HTMLElement): void { diff --git a/src/vs/workbench/browser/parts/editor/media/editordroptarget.css b/src/vs/workbench/browser/parts/editor/media/editordroptarget.css index 812d42d7611..e4649861605 100644 --- a/src/vs/workbench/browser/parts/editor/media/editordroptarget.css +++ b/src/vs/workbench/browser/parts/editor/media/editordroptarget.css @@ -28,7 +28,7 @@ #monaco-workbench-editor-drop-overlay .editor-group-overlay-drop-into-prompt { text-align: center; - padding: 1em; + padding: 0.6em; opacity: 0; /* hidden initially */ transition: opacity 150ms ease-out; } diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index fb7dcc2c042..efd9c628e99 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -279,6 +279,27 @@ export const EDITOR_DRAG_AND_DROP_BACKGROUND = registerColor('editorGroup.dropBa hcLight: Color.fromHex('#0F4A85').transparent(0.50) }, localize('editorDragAndDropBackground', "Background color when dragging editors around. The color should have transparency so that the editor contents can still shine through.")); +export const EDITOR_DROP_INTO_PROMPT_FOREGROUND = registerColor('editorGroup.dropIntoPromptForeground', { + dark: editorWidgetForeground, + light: editorWidgetForeground, + hcDark: editorWidgetForeground, + hcLight: editorWidgetForeground +}, localize('editorDropIntoPromptForeground', "Foreground color of text shown over editors when dragging files. This text informs the user that they can hold shift to drop into the editor.")); + +export const EDITOR_DROP_INTO_PROMPT_BACKGROUND = registerColor('editorGroup.dropIntoPromptBackground', { + dark: editorWidgetBackground, + light: editorWidgetBackground, + hcDark: editorWidgetBackground, + hcLight: editorWidgetBackground +}, localize('editorDropIntoPromptBackground', "Background color of text shown over editors when dragging files. This text informs the user that they can hold shift to drop into the editor.")); + +export const EDITOR_DROP_INTO_PROMPT_BORDER = registerColor('editorGroup.dropIntoPromptBorder', { + dark: null, + light: null, + hcDark: contrastBorder, + hcLight: contrastBorder +}, localize('editorDropIntoPromptBorder', "Border color of text shown over editors when dragging files. This text informs the user that they can hold shift to drop into the editor.")); + export const SIDE_BY_SIDE_EDITOR_HORIZONTAL_BORDER = registerColor('sideBySideEditor.horizontalBorder', { dark: EDITOR_GROUP_BORDER, light: EDITOR_GROUP_BORDER, From bf2a8fc56490c6f61e24c2469426dfb938a1c068 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 14 Apr 2022 11:30:46 -0700 Subject: [PATCH 093/245] Fixing line height if drop into prompt wraps --- .../workbench/browser/parts/editor/media/editordroptarget.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/browser/parts/editor/media/editordroptarget.css b/src/vs/workbench/browser/parts/editor/media/editordroptarget.css index e4649861605..7dd97524076 100644 --- a/src/vs/workbench/browser/parts/editor/media/editordroptarget.css +++ b/src/vs/workbench/browser/parts/editor/media/editordroptarget.css @@ -29,6 +29,8 @@ #monaco-workbench-editor-drop-overlay .editor-group-overlay-drop-into-prompt { text-align: center; padding: 0.6em; + margin: 0.2em; + line-height: normal; opacity: 0; /* hidden initially */ transition: opacity 150ms ease-out; } From 9fe5271dac26ed9ebdf6b2ae24016d8cfb8aa9fc Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 14 Apr 2022 11:46:04 -0700 Subject: [PATCH 094/245] tweak install from marketplace entry on kernel quickpick. --- .../editorStatusBar/editorStatusBar.ts | 22 +++++++++---------- .../view/renderers/backLayerWebView.ts | 6 ++--- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts index c93c6b17ba2..920669194f6 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts @@ -28,7 +28,7 @@ import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/note import { configureKernelIcon, selectKernelIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { INotebookKernel, INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { INotebookKernel, INotebookKernelService, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; @@ -182,12 +182,7 @@ registerAction2(class extends Action2 { return res; } const quickPickItems: QuickPickInput[] = []; - if (!all.length) { - quickPickItems.push({ - id: 'install', - label: nls.localize('installKernels', "Install kernels from the marketplace"), - }); - } else { + if (all.length) { // Always display suggested kernels on the top. if (suggestions.length) { quickPickItems.push({ @@ -197,11 +192,6 @@ registerAction2(class extends Action2 { quickPickItems.push(...suggestions.map(toQuickPick)); } - quickPickItems.push({ - id: 'install', - label: nls.localize('installKernels', "Install kernels from the marketplace"), - }); - // Next display all of the kernels grouped by categories or extensions. // If we don't have a kind, always display those at the bottom. const picks = all.filter(item => !suggestions.includes(item)).map(toQuickPick); @@ -215,6 +205,14 @@ registerAction2(class extends Action2 { }); } + if (!all.find(item => item.type === NotebookKernelType.Resolved)) { + // there is no resolved kernel, show the install from marketplace + quickPickItems.push({ + id: 'install', + label: nls.localize('installKernels', "Install kernels from the marketplace"), + }); + } + const pick = await quickInputService.pick(quickPickItems, { placeHolder: selected ? nls.localize('prompt.placeholder.change', "Change kernel for '{0}'", labelService.getUriLabel(notebook.uri, { relative: true })) diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index f7a64b5b596..a7145c9ffc9 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -904,7 +904,7 @@ var requirejs = (function() { } this._preloadsCache.clear(); - if (this._currentKernel && this._currentKernel.type === NotebookKernelType.Resolved) { + if (this._currentKernel?.type === NotebookKernelType.Resolved) { this._updatePreloadsFromKernel(this._currentKernel); } @@ -1401,7 +1401,7 @@ var requirejs = (function() { const previousKernel = this._currentKernel; this._currentKernel = kernel; - if (previousKernel && previousKernel.type === NotebookKernelType.Resolved && previousKernel.preloadUris.length > 0) { + if (previousKernel?.type === NotebookKernelType.Resolved && previousKernel.preloadUris.length > 0) { this.webview?.reload(); // preloads will be restored after reload } else if (kernel?.type === NotebookKernelType.Resolved) { this._updatePreloadsFromKernel(kernel); @@ -1434,7 +1434,7 @@ var requirejs = (function() { const mixedResourceRoots = [ ...(this.localResourceRootsCache || []), - ...(this._currentKernel && this._currentKernel.type === NotebookKernelType.Resolved ? [this._currentKernel.localResourceRoot] : []), + ...(this._currentKernel?.type === NotebookKernelType.Resolved ? [this._currentKernel.localResourceRoot] : []), ]; this.webview.localResourcesRoot = mixedResourceRoots; From ed1eaf32f845e452f76b824ed939bb89293c56b8 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 14 Apr 2022 12:00:40 -0700 Subject: [PATCH 095/245] Use keybinding style for `shift` in overlay --- .../browser/parts/editor/media/editordroptarget.css | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/vs/workbench/browser/parts/editor/media/editordroptarget.css b/src/vs/workbench/browser/parts/editor/media/editordroptarget.css index 7dd97524076..8a50b279785 100644 --- a/src/vs/workbench/browser/parts/editor/media/editordroptarget.css +++ b/src/vs/workbench/browser/parts/editor/media/editordroptarget.css @@ -35,6 +35,15 @@ transition: opacity 150ms ease-out; } +#monaco-workbench-editor-drop-overlay .editor-group-overlay-drop-into-prompt i /* Style keybinding */ { + padding: 0 8px; + border: 1px solid hsla(0,0%,80%,.4); + margin: 0 1px; + border-radius: 5px; + background-color: rgba(255, 255, 255, 0.05); + font-style: normal; +} + #monaco-workbench-editor-drop-overlay > .editor-group-overlay-indicator.overlay-move-transition { transition: top 70ms ease-out, left 70ms ease-out, width 70ms ease-out, height 70ms ease-out, opacity 150ms ease-out; } From 7195f05a16a5e92147354c4ebfd040b1eb60d162 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 14 Apr 2022 12:05:38 -0700 Subject: [PATCH 096/245] allow notebook cancel execution with proxy kernel. --- .../browser/notebookExecutionServiceImpl.ts | 25 ++++++++++++++----- .../viewParts/notebookKernelActionViewItem.ts | 3 +-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts index b8877a553a2..2d4fe4cebe0 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookExecutionServiceImpl.ts @@ -4,6 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { ILogService } from 'vs/platform/log/common/log'; import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust'; @@ -14,8 +16,9 @@ import { INotebookExecutionService } from 'vs/workbench/contrib/notebook/common/ import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; import { INotebookKernelService, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; -export class NotebookExecutionService implements INotebookExecutionService { +export class NotebookExecutionService implements INotebookExecutionService, IDisposable { declare _serviceBrand: undefined; + private _activeProxyKernelExecutionToken: CancellationTokenSource | undefined; constructor( @ICommandService private readonly _commandService: ICommandService, @@ -46,9 +49,10 @@ export class NotebookExecutionService implements INotebookExecutionService { } if (kernel.type === NotebookKernelType.Proxy) { - // we should actually resolve the kernel + this._activeProxyKernelExecutionToken?.dispose(true); + const tokenSource = this._activeProxyKernelExecutionToken = new CancellationTokenSource(); const resolved = await kernel.resolveKernel(notebook.uri); - let kernels = this._notebookKernelService.getMatchingKernel(notebook); + const kernels = this._notebookKernelService.getMatchingKernel(notebook); const newlyMatchedKernel = kernels.all.find(k => k.id === resolved); if (!newlyMatchedKernel) { @@ -56,6 +60,11 @@ export class NotebookExecutionService implements INotebookExecutionService { } kernel = newlyMatchedKernel; + if (tokenSource.token.isCancellationRequested) { + // execution was cancelled but we still need to update the active kernel + this._notebookKernelService.selectKernelForNotebook(kernel, notebook); + return; + } } if (kernel.type === NotebookKernelType.Proxy) { @@ -93,15 +102,19 @@ export class NotebookExecutionService implements INotebookExecutionService { const kernel = this._notebookKernelService.getSelectedOrSuggestedKernel(notebook); if (kernel) { if (kernel.type === NotebookKernelType.Proxy) { - // we should handle cancelling proxy kernel too - return; + this._activeProxyKernelExecutionToken?.dispose(true); + } else { + await kernel.cancelNotebookCellExecution(notebook.uri, cellsArr); } - await kernel.cancelNotebookCellExecution(notebook.uri, cellsArr); } } async cancelNotebookCells(notebook: INotebookTextModel, cells: Iterable): Promise { this.cancelNotebookCellHandles(notebook, Array.from(cells, cell => cell.handle)); } + + dispose() { + this._activeProxyKernelExecutionToken?.dispose(true); + } } diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts index a4060aaa62e..ed9098ad49d 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelActionViewItem.ts @@ -68,7 +68,7 @@ export class NotebooKernelActionViewItem extends ActionViewItem { private _updateActionFromKernelInfo(info: INotebookKernelMatchResult): void { this._kernelDisposable.clear(); this._action.enabled = true; - const selectedOrSuggested = info.selected ?? ((info.all.length === 1 && info.suggestions.length === 1 && !('resolveKernel' in info.suggestions[0])) ? info.suggestions[0] : undefined); + const selectedOrSuggested = info.selected ?? ((info.all.length === 1 && info.suggestions.length === 1 && info.suggestions[0].type === NotebookKernelType.Resolved) ? info.suggestions[0] : undefined); if (selectedOrSuggested) { // selected or suggested kernel this._action.label = selectedOrSuggested.label; @@ -94,7 +94,6 @@ export class NotebooKernelActionViewItem extends ActionViewItem { } })); } - } else { // many kernels or no kernels this._action.label = localize('select', "Select Kernel"); From cb80d2cb1d229a154a7bceef793704d0ec170488 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 14 Apr 2022 12:10:15 -0700 Subject: [PATCH 097/245] if resolve kernel failed, reset connection state --- .../browser/mainThreadNotebookProxyKernels.ts | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadNotebookProxyKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookProxyKernels.ts index 3db46989937..74e7290161d 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookProxyKernels.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookProxyKernels.ts @@ -9,6 +9,7 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; import { INotebookKernelService, INotebookProxyKernel, INotebookProxyKernelChangeEvent, ProxyKernelState, NotebookKernelType } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { ExtHostContext, ExtHostNotebookProxyKernelsShape, INotebookProxyKernelDto, MainContext, MainThreadNotebookProxyKernelsShape } from '../common/extHost.protocol'; +import { onUnexpectedError } from 'vs/base/common/errors'; abstract class MainThreadProxyKernel implements INotebookProxyKernel { readonly type: NotebookKernelType.Proxy = NotebookKernelType.Proxy; @@ -92,12 +93,19 @@ export class MainThreadNotebookProxyKernels implements MainThreadNotebookProxyKe const that = this; const proxyKernel = new class extends MainThreadProxyKernel { async resolveKernel(): Promise { - this.connectionState = ProxyKernelState.Initializing; - this._onDidChange.fire({ connectionState: true }); - const delegateKernel = await that._proxyKernelProxy.$resolveKernel(handle); - this.connectionState = ProxyKernelState.Connected; - this._onDidChange.fire({ connectionState: true }); - return delegateKernel; + try { + this.connectionState = ProxyKernelState.Initializing; + this._onDidChange.fire({ connectionState: true }); + const delegateKernel = await that._proxyKernelProxy.$resolveKernel(handle); + this.connectionState = ProxyKernelState.Connected; + this._onDidChange.fire({ connectionState: true }); + return delegateKernel; + } catch (err) { + onUnexpectedError(err); + this.connectionState = ProxyKernelState.Disconnected; + this._onDidChange.fire({ connectionState: true }); + return null; + } } }(data); From 5919378269660ca153e4a19e015bc00104d6e934 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Thu, 14 Apr 2022 12:18:25 -0700 Subject: [PATCH 098/245] don't surface error on gets and remove microsoft secret migration. Fixes #146553 --- .../src/common/keychain.ts | 8 --- .../microsoft-authentication/src/AADHelper.ts | 31 ---------- .../microsoft-authentication/src/keychain.ts | 60 ------------------- .../common/credentialsMainService.ts | 46 ++++++++++++-- .../electron-main/credentialsMainService.ts | 15 +++-- .../node/credentialsMainService.ts | 3 + 6 files changed, 51 insertions(+), 112 deletions(-) delete mode 100644 extensions/microsoft-authentication/src/keychain.ts diff --git a/extensions/github-authentication/src/common/keychain.ts b/extensions/github-authentication/src/common/keychain.ts index 760ae8f3f27..c7b36d96212 100644 --- a/extensions/github-authentication/src/common/keychain.ts +++ b/extensions/github-authentication/src/common/keychain.ts @@ -6,11 +6,8 @@ // keytar depends on a native module shipped in vscode, so this is // how we load it import * as vscode from 'vscode'; -import * as nls from 'vscode-nls'; import { Log } from './logger'; -const localize = nls.loadMessageBundle(); - export class Keychain { constructor( private readonly context: vscode.ExtensionContext, @@ -24,11 +21,6 @@ export class Keychain { } catch (e) { // Ignore this.Logger.error(`Setting token failed: ${e}`); - const troubleshooting = localize('troubleshooting', "Troubleshooting Guide"); - const result = await vscode.window.showErrorMessage(localize('keychainWriteError', "Writing login information to the keychain failed with error '{0}'.", e.message), troubleshooting); - if (result === troubleshooting) { - vscode.env.openExternal(vscode.Uri.parse('https://code.visualstudio.com/docs/editor/settings-sync#_troubleshooting-keychain-issues')); - } } } diff --git a/extensions/microsoft-authentication/src/AADHelper.ts b/extensions/microsoft-authentication/src/AADHelper.ts index 8321b5682be..6cd4d0f684b 100644 --- a/extensions/microsoft-authentication/src/AADHelper.ts +++ b/extensions/microsoft-authentication/src/AADHelper.ts @@ -10,7 +10,6 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import { v4 as uuid } from 'uuid'; import fetch, { Response } from 'node-fetch'; -import { Keychain } from './keychain'; import Logger from './logger'; import { toBase64UrlEncoding } from './utils'; import { sha256 } from './env/node/sha256'; @@ -125,10 +124,6 @@ export class AzureActiveDirectoryService { let sessions = await this._tokenStorage.getAll(); Logger.info(`Got ${sessions.length} stored sessions`); - if (!sessions.length) { - sessions = await this.migrate(); - } - const refreshes = sessions.map(async session => { Logger.trace(`Read the following stored session with scopes: ${session.scope}`); const scopes = session.scope.split(' '); @@ -813,32 +808,6 @@ export class AzureActiveDirectoryService { } } - private async migrate() { - Logger.info('Attempting to migrate stored sessions.'); - const migrated = this._context.globalState.get<{ migrated: boolean }>('microsoft-better-storage-layout-migrated'); - if (migrated?.migrated) { - return []; - } - await this._context.globalState.update('microsoft-better-storage-layout-migrated', { migrated: true }); - const keychain = new Keychain(this._context); - const storedData = await keychain.getToken(); - if (!storedData) { - Logger.info('No stored sessions found.'); - return []; - } - - try { - const sessions = JSON.parse(storedData) as IStoredSession[]; - Logger.info(`Migrated ${sessions.length} stored sessions.`); - return sessions; - } catch (e) { - Logger.info('Failed to parse stored sessions. Migrating no sessions.'); - return []; - } finally { - await keychain.deleteToken(); - } - } - //#endregion //#region static methods diff --git a/extensions/microsoft-authentication/src/keychain.ts b/extensions/microsoft-authentication/src/keychain.ts deleted file mode 100644 index f9704892887..00000000000 --- a/extensions/microsoft-authentication/src/keychain.ts +++ /dev/null @@ -1,60 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import Logger from './logger'; -import * as nls from 'vscode-nls'; - -const localize = nls.loadMessageBundle(); - -const SERVICE_ID = `microsoft.login`; - -export class Keychain { - - constructor(private context: vscode.ExtensionContext) { } - - async setToken(token: string): Promise { - - try { - return await this.context.secrets.store(SERVICE_ID, token); - } catch (e) { - Logger.error(`Setting token failed: ${e}`); - - // Temporary fix for #94005 - // This happens when processes write simulatenously to the keychain, most - // likely when trying to refresh the token. Ignore the error since additional - // writes after the first one do not matter. Should actually be fixed upstream. - if (e.message === 'The specified item already exists in the keychain.') { - return; - } - - const troubleshooting = localize('troubleshooting', "Troubleshooting Guide"); - const result = await vscode.window.showErrorMessage(localize('keychainWriteError', "Writing login information to the keychain failed with error '{0}'.", e.message), troubleshooting); - if (result === troubleshooting) { - vscode.env.openExternal(vscode.Uri.parse('https://code.visualstudio.com/docs/editor/settings-sync#_troubleshooting-keychain-issues')); - } - } - } - - async getToken(): Promise { - try { - return await this.context.secrets.get(SERVICE_ID); - } catch (e) { - // Ignore - Logger.error(`Getting token failed: ${e}`); - return Promise.resolve(undefined); - } - } - - async deleteToken(): Promise { - try { - return await this.context.secrets.delete(SERVICE_ID); - } catch (e) { - // Ignore - Logger.error(`Deleting token failed: ${e}`); - return Promise.resolve(undefined); - } - } -} diff --git a/src/vs/platform/credentials/common/credentialsMainService.ts b/src/vs/platform/credentials/common/credentialsMainService.ts index 6bed808b144..912cb0fb5c6 100644 --- a/src/vs/platform/credentials/common/credentialsMainService.ts +++ b/src/vs/platform/credentials/common/credentialsMainService.ts @@ -37,11 +37,22 @@ export abstract class BaseCredentialsMainService extends Disposable implements I public abstract getSecretStoragePrefix(): Promise; protected abstract withKeytar(): Promise; + /** + * An optional method that subclasses can implement to assist in surfacing + * Keytar load errors to the user in a friendly way. + */ + protected abstract surfaceKeytarLoadError?: (err: any) => void; //#endregion async getPassword(service: string, account: string): Promise { - const keytar = await this.withKeytar(); + let keytar: KeytarModule; + try { + keytar = await this.withKeytar(); + } catch (e) { + // for get operations, we don't want to surface errors to the user + return null; + } const password = await keytar.getPassword(service, account); if (password) { @@ -70,7 +81,14 @@ export abstract class BaseCredentialsMainService extends Disposable implements I } async setPassword(service: string, account: string, password: string): Promise { - const keytar = await this.withKeytar(); + let keytar: KeytarModule; + try { + keytar = await this.withKeytar(); + } catch (e) { + this.surfaceKeytarLoadError?.(e); + throw e; + } + const MAX_SET_ATTEMPTS = 3; // Sometimes Keytar has a problem talking to the keychain on the OS. To be more resilient, we retry a few times. @@ -119,7 +137,13 @@ export abstract class BaseCredentialsMainService extends Disposable implements I } async deletePassword(service: string, account: string): Promise { - const keytar = await this.withKeytar(); + let keytar: KeytarModule; + try { + keytar = await this.withKeytar(); + } catch (e) { + this.surfaceKeytarLoadError?.(e); + throw e; + } const didDelete = await keytar.deletePassword(service, account); if (didDelete) { @@ -130,13 +154,25 @@ export abstract class BaseCredentialsMainService extends Disposable implements I } async findPassword(service: string): Promise { - const keytar = await this.withKeytar(); + let keytar: KeytarModule; + try { + keytar = await this.withKeytar(); + } catch (e) { + // for get operations, we don't want to surface errors to the user + return null; + } return keytar.findPassword(service); } async findCredentials(service: string): Promise> { - const keytar = await this.withKeytar(); + let keytar: KeytarModule; + try { + keytar = await this.withKeytar(); + } catch (e) { + // for get operations, we don't want to surface errors to the user + return []; + } return keytar.findCredentials(service); } diff --git a/src/vs/platform/credentials/electron-main/credentialsMainService.ts b/src/vs/platform/credentials/electron-main/credentialsMainService.ts index 0bf232d2153..5d782da694d 100644 --- a/src/vs/platform/credentials/electron-main/credentialsMainService.ts +++ b/src/vs/platform/credentials/electron-main/credentialsMainService.ts @@ -36,14 +36,13 @@ export class CredentialsNativeMainService extends BaseCredentialsMainService { return this._keytarCache; } - try { - this._keytarCache = await import('keytar'); - // Try using keytar to see if it throws or not. - await this._keytarCache.findCredentials('test-keytar-loads'); - } catch (e) { - this.windowsMainService.sendToFocused('vscode:showCredentialsError', e.message ?? e); - throw e; - } + this._keytarCache = await import('keytar'); + // Try using keytar to see if it throws or not. + await this._keytarCache.findCredentials('test-keytar-loads'); return this._keytarCache; } + + protected override surfaceKeytarLoadError = (err: any) => { + this.windowsMainService.sendToFocused('vscode:showCredentialsError', err.message ?? err); + }; } diff --git a/src/vs/platform/credentials/node/credentialsMainService.ts b/src/vs/platform/credentials/node/credentialsMainService.ts index f00f3532d00..cc2156d22cd 100644 --- a/src/vs/platform/credentials/node/credentialsMainService.ts +++ b/src/vs/platform/credentials/node/credentialsMainService.ts @@ -10,6 +10,9 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { BaseCredentialsMainService, KeytarModule } from 'vs/platform/credentials/common/credentialsMainService'; export class CredentialsWebMainService extends BaseCredentialsMainService { + // Since we fallback to the in-memory credentials provider, we do not need to surface any Keytar load errors + // to the user. + protected surfaceKeytarLoadError?: (err: any) => void; constructor( @ILogService logService: ILogService, From c3d09dbabf4a9dac6c2a8077a0f3744fd55beb95 Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Thu, 14 Apr 2022 13:48:06 -0700 Subject: [PATCH 099/245] Polish language filter button implementation (#147461) --- .../suggestEnabledInput/suggestEnabledInput.ts | 2 +- .../contrib/preferences/browser/settingsEditor2.ts | 6 +++--- .../preferences/browser/settingsSearchMenu.ts | 13 ++++++++----- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts index 138f276bff7..9cadf75ecce 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts @@ -111,7 +111,7 @@ export class SuggestEnabledInput extends Widget implements IThemable { private readonly _onInputDidChange = new Emitter(); readonly onInputDidChange: Event = this._onInputDidChange.event; - protected readonly inputWidget: CodeEditorWidget; + readonly inputWidget: CodeEditorWidget; private readonly inputModel: ITextModel; protected stylingContainer: HTMLDivElement; private placeholderText: HTMLDivElement; diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 392abf569fe..d71c2e6acec 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -604,9 +604,9 @@ export class SettingsEditor2 extends EditorPane { const actionBar = this._register(new ActionBar(this.controlsElement, { animated: false, - actionViewItemProvider: (_action) => { - if (_action.id === filterAction.id) { - return this.instantiationService.createInstance(SettingsSearchFilterDropdownMenuActionViewItem, _action, this.actionRunner, this.searchWidget); + actionViewItemProvider: (action) => { + if (action.id === filterAction.id) { + return this.instantiationService.createInstance(SettingsSearchFilterDropdownMenuActionViewItem, action, this.actionRunner, this.searchWidget); } return undefined; } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts b/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts index d739a8752f5..b8b09ebc8ff 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts @@ -6,19 +6,20 @@ import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; import { IAction, IActionRunner } from 'vs/base/common/actions'; +import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController'; import { localize } from 'vs/nls'; -import { ICommandService } from 'vs/platform/commands/common/commands'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { SuggestEnabledInput } from 'vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput'; import { EXTENSION_SETTING_TAG, FEATURE_SETTING_TAG, GENERAL_TAG_SETTING_TAG, ID_SETTING_TAG, LANGUAGE_SETTING_TAG, MODIFIED_SETTING_TAG } from 'vs/workbench/contrib/preferences/common/preferences'; export class SettingsSearchFilterDropdownMenuActionViewItem extends DropdownMenuActionViewItem { + private readonly suggestController: SuggestController | null; + constructor( action: IAction, actionRunner: IActionRunner | undefined, private readonly searchWidget: SuggestEnabledInput, - @IContextMenuService contextMenuService: IContextMenuService, - @ICommandService private readonly commandService: ICommandService + @IContextMenuService contextMenuService: IContextMenuService ) { super(action, { getActions: () => this.getActions() }, @@ -30,6 +31,8 @@ export class SettingsSearchFilterDropdownMenuActionViewItem extends DropdownMenu menuAsChild: true } ); + + this.suggestController = SuggestController.get(this.searchWidget.inputWidget); } override render(container: HTMLElement): void { @@ -39,8 +42,8 @@ export class SettingsSearchFilterDropdownMenuActionViewItem extends DropdownMenu private doSearchWidgetAction(queryToAppend: string, triggerSuggest: boolean) { this.searchWidget.setValue(this.searchWidget.getValue().trimEnd() + ' ' + queryToAppend); this.searchWidget.focus(); - if (triggerSuggest) { - this.commandService.executeCommand('editor.action.triggerSuggest'); + if (triggerSuggest && this.suggestController) { + this.suggestController.triggerSuggest(); } } From ab174654a59846e885562ba2defcd1764677de5c Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Thu, 14 Apr 2022 13:51:57 -0700 Subject: [PATCH 100/245] improve select a different editor command (#147479) --- .../browser/untitledTextEditorHint.ts | 6 ++--- .../common/newFile.contribution.ts | 22 ++++++++++++++----- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint.ts b/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint.ts index f4fd71ba538..c5e1a0cba17 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint.ts @@ -156,14 +156,14 @@ class UntitledTextEditorHintContentWidget implements IContentWidget { const chooseEditorOnClickOrTap = async (e: MouseEvent) => { e.stopPropagation(); + const activeEditorInput = this.editorGroupsService.activeGroup.activeEditor; + const newEditorSelected = await this.commandService.executeCommand('welcome.showNewFileEntries', { from: 'hint' }); // Close the active editor as long as it is untitled (swap the editors out) - if (activeEditorInput !== null && activeEditorInput.resource?.scheme === Schemas.untitled) { + if (newEditorSelected && activeEditorInput !== null && activeEditorInput.resource?.scheme === Schemas.untitled) { this.editorGroupsService.activeGroup.closeEditor(activeEditorInput, { preserveFocus: true }); } - - await this.commandService.executeCommand('welcome.showNewFileEntries', { from: 'hint' }); }; this.toDispose.push(dom.addDisposableListener(chooseEditor, 'click', chooseEditorOnClickOrTap)); this.toDispose.push(dom.addDisposableListener(chooseEditor, GestureEventType.Tap, chooseEditorOnClickOrTap)); diff --git a/src/vs/workbench/contrib/welcomeViews/common/newFile.contribution.ts b/src/vs/workbench/contrib/welcomeViews/common/newFile.contribution.ts index 1fe5734dd99..3c20b55f28a 100644 --- a/src/vs/workbench/contrib/welcomeViews/common/newFile.contribution.ts +++ b/src/vs/workbench/contrib/welcomeViews/common/newFile.contribution.ts @@ -40,8 +40,8 @@ registerAction2(class extends Action2 { }); } - run(accessor: ServicesAccessor) { - assertIsDefined(NewFileTemplatesManager.Instance).run(); + async run(accessor: ServicesAccessor): Promise { + return assertIsDefined(NewFileTemplatesManager.Instance).run(); } }); @@ -79,20 +79,26 @@ class NewFileTemplatesManager extends Disposable { return items; } - run() { + async run(): Promise { const entries = this.allEntries(); if (entries.length === 0) { throw Error('Unexpected empty new items list'); } else if (entries.length === 1) { this.commandService.executeCommand(entries[0].commandID); + return true; } else { - this.selectNewEntry(entries); + return this.selectNewEntry(entries); } } - private async selectNewEntry(entries: NewFileItem[]) { + private async selectNewEntry(entries: NewFileItem[]): Promise { + let resolveResult: (res: boolean) => void; + const resultPromise = new Promise(resolve => { + resolveResult = resolve; + }); + const disposables = new DisposableStore(); const qp = this.quickInputService.createQuickPick(); qp.title = localize('createNew', "Create New..."); @@ -158,6 +164,8 @@ class NewFileTemplatesManager extends Disposable { disposables.add(qp.onDidAccept(async e => { const selected = qp.selectedItems[0] as (IQuickPickItem & NewFileItem); + resolveResult(!!selected); + qp.hide(); if (selected) { await this.commandService.executeCommand(selected.commandID); } })); @@ -165,14 +173,18 @@ class NewFileTemplatesManager extends Disposable { disposables.add(qp.onDidHide(() => { qp.dispose(); disposables.dispose(); + resolveResult(false); })); disposables.add(qp.onDidTriggerItemButton(e => { qp.hide(); this.commandService.executeCommand('workbench.action.openGlobalKeybindings', (e.item as (IQuickPickItem & NewFileItem)).commandID); + resolveResult(false); })); qp.show(); + + return resultPromise; } } From cc7423817340354a2583893e967a4c2184bf5bd4 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 14 Apr 2022 12:58:26 -0800 Subject: [PATCH 101/245] Update to build VS Code using latest TS nightly (#146933) * Fixing line height if drop into prompt wraps * Use keybinding style for `shift` in overlay * Update to build VS Code using latest TS nightly * Fix compile error * Bump TS * Add constraint --- build/lib/nls.ts | 2 +- package.json | 2 +- .../test/browser/view/minimapCharRenderer.test.ts | 1 + yarn.lock | 10 +++++----- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/build/lib/nls.ts b/build/lib/nls.ts index 00f153acfc8..7c980a43a44 100644 --- a/build/lib/nls.ts +++ b/build/lib/nls.ts @@ -40,7 +40,7 @@ function collect(ts: typeof import('typescript'), node: ts.Node, fn: (node: ts.N return result; } -function clone(object: T): T { +function clone(object: T): T { const result = {}; for (const id in object) { result[id] = object[id]; diff --git a/package.json b/package.json index 6071eeb9f33..f3462182339 100644 --- a/package.json +++ b/package.json @@ -200,7 +200,7 @@ "style-loader": "^1.0.0", "ts-loader": "^9.2.7", "tsec": "0.1.4", - "typescript": "4.7.0-dev.20220323", + "typescript": "^4.7.0-dev.20220408", "typescript-formatter": "7.1.0", "underscore": "^1.12.1", "util": "^0.12.4", diff --git a/src/vs/editor/test/browser/view/minimapCharRenderer.test.ts b/src/vs/editor/test/browser/view/minimapCharRenderer.test.ts index c8d33c13f95..3359f968f5b 100644 --- a/src/vs/editor/test/browser/view/minimapCharRenderer.test.ts +++ b/src/vs/editor/test/browser/view/minimapCharRenderer.test.ts @@ -58,6 +58,7 @@ suite('MinimapCharRenderer', () => { function createFakeImageData(width: number, height: number): ImageData { return { + colorSpace: 'srgb', width: width, height: height, data: new Uint8ClampedArray(width * height * Constants.RGBA_CHANNELS_CNT) diff --git a/yarn.lock b/yarn.lock index d22deb1d491..9fec7f7d776 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11399,16 +11399,16 @@ typescript-formatter@7.1.0: commandpost "^1.0.0" editorconfig "^0.15.0" -typescript@4.7.0-dev.20220323: - version "4.7.0-dev.20220323" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.0-dev.20220323.tgz#eb9dd18f77a7fd58bbd53ce8d7dc86bdcda87e40" - integrity sha512-fJQwwIgPcAJV9qn45kgIaiiklW6eY+SuTVAM2WgNAJS7pGN6mfuUB7sk0G+HGTsWWI/gMlGVgNdPY3jK4M7Hfg== - typescript@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4" integrity sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q= +typescript@^4.7.0-dev.20220408: + version "4.7.0-dev.20220408" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.0-dev.20220408.tgz#52a049ad6c3c78856935692c3fc69aec8a40ec6d" + integrity sha512-2KgXRQXrjFEbm+ismaGoR803nTRQuc03XQ1crnlxXvQYIXasPiidwiczLc9i0rTo33KXIN2M6YiNtn1tZihHgw== + typical@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4" From ac00898e447e8d5ea4b0ad120671fdb78622f7ab Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Thu, 14 Apr 2022 14:26:15 -0700 Subject: [PATCH 102/245] fix: compare origins in fetch requests against remote authority (#147467) The previous implementation did not consider what would happen if webview resources were served from the same domain. By first comparing the requestUrl.orgin with the sw.orgin (similar to how it's done for localhost), this is no longer a problem. And since the requests have the same origin, authentication will never be an issue as cookies will exist. --- src/vs/workbench/common/webview.ts | 2 +- .../workbench/contrib/webview/browser/pre/service-worker.js | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/common/webview.ts b/src/vs/workbench/common/webview.ts index aa598f1133d..1db1d451aff 100644 --- a/src/vs/workbench/common/webview.ts +++ b/src/vs/workbench/common/webview.ts @@ -22,7 +22,7 @@ export const webviewResourceBaseHost = 'vscode-cdn.net'; export const webviewRootResourceAuthority = `vscode-resource.${webviewResourceBaseHost}`; -export const webviewGenericCspSource = `https://*.${webviewResourceBaseHost}`; +export const webviewGenericCspSource = `'self' https://*.${webviewResourceBaseHost}`; /** * Construct a uri that can load resources inside a webview diff --git a/src/vs/workbench/contrib/webview/browser/pre/service-worker.js b/src/vs/workbench/contrib/webview/browser/pre/service-worker.js index 70534ee470d..81a45ab6ca7 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/service-worker.js +++ b/src/vs/workbench/contrib/webview/browser/pre/service-worker.js @@ -189,8 +189,10 @@ sw.addEventListener('fetch', (event) => { } // If we're making a request against the remote authority, we want to go - // back through VS Code itself so that we are authenticated properly - if (requestUrl.host === remoteAuthority) { + // through VS Code itself so that we are authenticated properly. If the + // service worker is hosted on the same origin we will have cookies and + // authentication will not be an issue. + if (requestUrl.origin !== sw.origin && requestUrl.host === remoteAuthority) { switch (event.request.method) { case 'GET': case 'HEAD': From 50ea16198d613629f7b57592a52ec834378f1c41 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 14 Apr 2022 14:39:44 -0700 Subject: [PATCH 103/245] Remove the concat text document api proposal Fixes #146781 --- extensions/vscode-api-tests/package.json | 1 - extensions/vscode-notebook-tests/package.json | 1 - .../workbench/api/common/extHost.api.impl.ts | 6 - .../common/extHostNotebookConcatDocument.ts | 192 ------ .../extHostNotebookConcatDocument.test.ts | 620 ------------------ .../common/extensionsApiProposals.ts | 1 - ...e.proposed.notebookConcatTextDocument.d.ts | 50 -- 7 files changed, 871 deletions(-) delete mode 100644 src/vs/workbench/api/common/extHostNotebookConcatDocument.ts delete mode 100644 src/vs/workbench/api/test/browser/extHostNotebookConcatDocument.test.ts delete mode 100644 src/vscode-dts/vscode.proposed.notebookConcatTextDocument.d.ts diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 22a274a9c68..2a1bad68677 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -19,7 +19,6 @@ "fsChunks", "inlineCompletions", "notebookCellExecutionState", - "notebookConcatTextDocument", "notebookContentProvider", "notebookControllerKind", "notebookDebugOptions", diff --git a/extensions/vscode-notebook-tests/package.json b/extensions/vscode-notebook-tests/package.json index e0a672ca179..8792cd4e6fb 100644 --- a/extensions/vscode-notebook-tests/package.json +++ b/extensions/vscode-notebook-tests/package.json @@ -11,7 +11,6 @@ "main": "./out/extension", "enabledApiProposals": [ "notebookCellExecutionState", - "notebookConcatTextDocument", "notebookContentProvider", "notebookControllerKind", "notebookDebugOptions", diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 668f8eb02c6..6cb1dd4fdfb 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -70,7 +70,6 @@ import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelServ import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; import { ExtHostAuthentication } from 'vs/workbench/api/common/extHostAuthentication'; import { ExtHostTimeline } from 'vs/workbench/api/common/extHostTimeline'; -import { ExtHostNotebookConcatDocument } from 'vs/workbench/api/common/extHostNotebookConcatDocument'; import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer'; import { ExtHostWebviewViews } from 'vs/workbench/api/common/extHostWebviewView'; @@ -1130,11 +1129,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension, 'notebookCellExecutionState'); return extHostNotebookKernels.onDidChangeNotebookCellExecutionState(listener, thisArgs, disposables); }, - createConcatTextDocument(notebook, selector) { - checkProposedApiEnabled(extension, 'notebookConcatTextDocument'); - extHostApiDeprecation.report('notebookConcatTextDocument', extension, 'This proposal is not on track for finalization and will be removed.'); - return new ExtHostNotebookConcatDocument(extHostNotebookDocuments, extHostDocuments, notebook, selector); - }, createNotebookProxyController(id: string, notebookType: string, label: string, handler: () => vscode.NotebookController | string | Thenable) { checkProposedApiEnabled(extension, 'notebookProxyController'); return extHostNotebookProxyKernels.createNotebookProxyController(extension, id, notebookType, label, handler); diff --git a/src/vs/workbench/api/common/extHostNotebookConcatDocument.ts b/src/vs/workbench/api/common/extHostNotebookConcatDocument.ts deleted file mode 100644 index 65ecebc97c1..00000000000 --- a/src/vs/workbench/api/common/extHostNotebookConcatDocument.ts +++ /dev/null @@ -1,192 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as types from 'vs/workbench/api/common/extHostTypes'; -import * as vscode from 'vscode'; -import { Event, Emitter } from 'vs/base/common/event'; -import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; -import { PrefixSumComputer } from 'vs/editor/common/model/prefixSumComputer'; -import { DisposableStore } from 'vs/base/common/lifecycle'; -import { score } from 'vs/editor/common/languageSelector'; -import { ResourceMap } from 'vs/base/common/map'; -import { URI } from 'vs/base/common/uri'; -import { generateUuid } from 'vs/base/common/uuid'; -import { ExtHostNotebookDocuments } from 'vs/workbench/api/common/extHostNotebookDocuments'; - -export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextDocument { - - private _disposables = new DisposableStore(); - private _isClosed = false; - - private _cells!: vscode.NotebookCell[]; - private _cellUris!: ResourceMap; - private _cellLengths!: PrefixSumComputer; - private _cellLines!: PrefixSumComputer; - private _versionId = 0; - - private readonly _onDidChange = new Emitter(); - readonly onDidChange: Event = this._onDidChange.event; - - readonly uri = URI.from({ scheme: 'vscode-concat-doc', path: generateUuid() }); - - constructor( - extHostNotebooks: ExtHostNotebookDocuments, - extHostDocuments: ExtHostDocuments, - private readonly _notebook: vscode.NotebookDocument, - private readonly _selector: vscode.DocumentSelector | undefined, - ) { - this._init(); - - this._disposables.add(extHostDocuments.onDidChangeDocument(e => { - const cellIdx = this._cellUris.get(e.document.uri); - if (cellIdx !== undefined) { - this._cellLengths.setValue(cellIdx, this._cells[cellIdx].document.getText().length + 1); - this._cellLines.setValue(cellIdx, this._cells[cellIdx].document.lineCount); - this._versionId += 1; - this._onDidChange.fire(undefined); - } - })); - const documentChange = (document: vscode.NotebookDocument) => { - if (document === this._notebook) { - this._init(); - this._versionId += 1; - this._onDidChange.fire(undefined); - } - }; - - this._disposables.add(extHostNotebooks.onDidChangeNotebookDocument(e => documentChange(e.notebook))); - } - - dispose(): void { - this._disposables.dispose(); - this._isClosed = true; - } - - get isClosed() { - return this._isClosed; - } - - private _init() { - this._cells = []; - this._cellUris = new ResourceMap(); - const cellLengths: number[] = []; - const cellLineCounts: number[] = []; - for (const cell of this._notebook.getCells()) { - if (cell.kind === types.NotebookCellKind.Code && (!this._selector || score(this._selector, cell.document.uri, cell.document.languageId, true, undefined))) { - this._cellUris.set(cell.document.uri, this._cells.length); - this._cells.push(cell); - cellLengths.push(cell.document.getText().length + 1); - cellLineCounts.push(cell.document.lineCount); - } - } - this._cellLengths = new PrefixSumComputer(new Uint32Array(cellLengths)); - this._cellLines = new PrefixSumComputer(new Uint32Array(cellLineCounts)); - } - - get version(): number { - return this._versionId; - } - - getText(range?: vscode.Range): string { - if (!range) { - let result = ''; - for (const cell of this._cells) { - result += cell.document.getText() + '\n'; - } - // remove last newline again - result = result.slice(0, -1); - return result; - } - - if (range.isEmpty) { - return ''; - } - - // get start and end locations and create substrings - const start = this.locationAt(range.start); - const end = this.locationAt(range.end); - - const startIdx = this._cellUris.get(start.uri); - const endIdx = this._cellUris.get(end.uri); - - if (startIdx === undefined || endIdx === undefined) { - return ''; - } - - if (startIdx === endIdx) { - return this._cells[startIdx].document.getText(new types.Range(start.range.start, end.range.end)); - } - - const parts = [this._cells[startIdx].document.getText(new types.Range(start.range.start, new types.Position(this._cells[startIdx].document.lineCount, 0)))]; - for (let i = startIdx + 1; i < endIdx; i++) { - parts.push(this._cells[i].document.getText()); - } - parts.push(this._cells[endIdx].document.getText(new types.Range(new types.Position(0, 0), end.range.end))); - return parts.join('\n'); - } - - offsetAt(position: vscode.Position): number { - const idx = this._cellLines.getIndexOf(position.line); - const offset1 = this._cellLengths.getPrefixSum(idx.index - 1); - const offset2 = this._cells[idx.index].document.offsetAt(position.with(idx.remainder)); - return offset1 + offset2; - } - - positionAt(locationOrOffset: vscode.Location | number): vscode.Position { - if (typeof locationOrOffset === 'number') { - const idx = this._cellLengths.getIndexOf(locationOrOffset); - const lineCount = this._cellLines.getPrefixSum(idx.index - 1); - return this._cells[idx.index].document.positionAt(idx.remainder).translate(lineCount); - } - - const idx = this._cellUris.get(locationOrOffset.uri); - if (idx !== undefined) { - const line = this._cellLines.getPrefixSum(idx - 1); - return new types.Position(line + locationOrOffset.range.start.line, locationOrOffset.range.start.character); - } - // do better? - // return undefined; - return new types.Position(0, 0); - } - - locationAt(positionOrRange: vscode.Range | vscode.Position): types.Location { - if (!types.Range.isRange(positionOrRange)) { - positionOrRange = new types.Range(positionOrRange, positionOrRange); - } - - const startIdx = this._cellLines.getIndexOf(positionOrRange.start.line); - let endIdx = startIdx; - if (!positionOrRange.isEmpty) { - endIdx = this._cellLines.getIndexOf(positionOrRange.end.line); - } - - const startPos = new types.Position(startIdx.remainder, positionOrRange.start.character); - const endPos = new types.Position(endIdx.remainder, positionOrRange.end.character); - const range = new types.Range(startPos, endPos); - - const startCell = this._cells[startIdx.index]; - return new types.Location(startCell.document.uri, startCell.document.validateRange(range)); - } - - contains(uri: vscode.Uri): boolean { - return this._cellUris.has(uri); - } - - validateRange(range: vscode.Range): vscode.Range { - const start = this.validatePosition(range.start); - const end = this.validatePosition(range.end); - return range.with(start, end); - } - - validatePosition(position: vscode.Position): vscode.Position { - const startIdx = this._cellLines.getIndexOf(position.line); - - const cellPosition = new types.Position(startIdx.remainder, position.character); - const validCellPosition = this._cells[startIdx.index].document.validatePosition(cellPosition); - - const line = this._cellLines.getPrefixSum(startIdx.index - 1); - return new types.Position(line + validCellPosition.line, validCellPosition.character); - } -} diff --git a/src/vs/workbench/api/test/browser/extHostNotebookConcatDocument.test.ts b/src/vs/workbench/api/test/browser/extHostNotebookConcatDocument.test.ts deleted file mode 100644 index 6267d249941..00000000000 --- a/src/vs/workbench/api/test/browser/extHostNotebookConcatDocument.test.ts +++ /dev/null @@ -1,620 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import { TestRPCProtocol } from 'vs/workbench/api/test/common/testRPCProtocol'; -import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; -import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; -import { NullLogService } from 'vs/platform/log/common/log'; -import { ExtHostNotebookConcatDocument } from 'vs/workbench/api/common/extHostNotebookConcatDocument'; -import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; -import { ExtHostNotebookDocument } from 'vs/workbench/api/common/extHostNotebookDocument'; -import { URI } from 'vs/base/common/uri'; -import { CellKind, CellUri, NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { Position, Location, Range } from 'vs/workbench/api/common/extHostTypes'; -import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; -import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; -import * as vscode from 'vscode'; -import { mock } from 'vs/workbench/test/common/workbenchTestServices'; -import { MainContext, MainThreadCommandsShape, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol'; -import { DisposableStore } from 'vs/base/common/lifecycle'; -import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; -import { generateUuid } from 'vs/base/common/uuid'; -import { ExtHostNotebookDocuments } from 'vs/workbench/api/common/extHostNotebookDocuments'; -import { SerializableObjectWithBuffers } from 'vs/workbench/services/extensions/common/proxyIdentifier'; - -suite('NotebookConcatDocument', function () { - - let rpcProtocol: TestRPCProtocol; - let notebook: ExtHostNotebookDocument; - let extHostDocumentsAndEditors: ExtHostDocumentsAndEditors; - let extHostDocuments: ExtHostDocuments; - let extHostNotebooks: ExtHostNotebookController; - let extHostNotebookDocuments: ExtHostNotebookDocuments; - - const notebookUri = URI.parse('test:///notebook.file'); - const disposables = new DisposableStore(); - - setup(async function () { - disposables.clear(); - - rpcProtocol = new TestRPCProtocol(); - rpcProtocol.set(MainContext.MainThreadCommands, new class extends mock() { - override $registerCommand() { } - }); - rpcProtocol.set(MainContext.MainThreadNotebook, new class extends mock() { - override async $registerNotebookProvider() { } - override async $unregisterNotebookProvider() { } - }); - extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(rpcProtocol, new NullLogService()); - extHostDocuments = new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors); - const extHostStoragePaths = new class extends mock() { - override workspaceValue() { - return URI.from({ scheme: 'test', path: generateUuid() }); - } - }; - extHostNotebooks = new ExtHostNotebookController(rpcProtocol, new ExtHostCommands(rpcProtocol, new NullLogService()), extHostDocumentsAndEditors, extHostDocuments, extHostStoragePaths); - extHostNotebookDocuments = new ExtHostNotebookDocuments(extHostNotebooks); - - let reg = extHostNotebooks.registerNotebookContentProvider(nullExtensionDescription, 'test', new class extends mock() { - // async openNotebook() { } - }); - extHostNotebooks.$acceptDocumentAndEditorsDelta(new SerializableObjectWithBuffers({ - addedDocuments: [{ - uri: notebookUri, - viewType: 'test', - cells: [{ - handle: 0, - uri: CellUri.generate(notebookUri, 0), - source: ['### Heading'], - eol: '\n', - language: 'markdown', - cellKind: CellKind.Markup, - outputs: [], - }], - versionId: 0 - }], - addedEditors: [{ - documentUri: notebookUri, - id: '_notebook_editor_0', - selections: [{ start: 0, end: 1 }], - visibleRanges: [] - }] - })); - extHostNotebooks.$acceptDocumentAndEditorsDelta(new SerializableObjectWithBuffers({ newActiveEditor: '_notebook_editor_0' })); - - notebook = extHostNotebooks.notebookDocuments[0]!; - - disposables.add(reg); - disposables.add(notebook); - disposables.add(extHostDocuments); - }); - - test('empty', function () { - let doc = new ExtHostNotebookConcatDocument(extHostNotebookDocuments, extHostDocuments, notebook.apiNotebook, undefined); - assert.strictEqual(doc.getText(), ''); - assert.strictEqual(doc.version, 0); - - // assert.strictEqual(doc.locationAt(new Position(0, 0)), undefined); - // assert.strictEqual(doc.positionAt(SOME_FAKE_LOCATION?), undefined); - }); - - - function assertLocation(doc: vscode.NotebookConcatTextDocument, pos: Position, expected: Location, reverse = true) { - const actual = doc.locationAt(pos); - assert.strictEqual(actual.uri.toString(), expected.uri.toString()); - assert.strictEqual(actual.range.isEqual(expected.range), true); - - if (reverse) { - // reverse - offset - const offset = doc.offsetAt(pos); - assert.strictEqual(doc.positionAt(offset).isEqual(pos), true); - - // reverse - pos - const actualPosition = doc.positionAt(actual); - assert.strictEqual(actualPosition.isEqual(pos), true); - } - } - - function assertLines(doc: vscode.NotebookConcatTextDocument, ...lines: string[]) { - let actual = doc.getText().split(/\r\n|\n|\r/); - assert.deepStrictEqual(actual, lines); - } - - test('contains', function () { - - const cellUri1 = CellUri.generate(notebook.uri, 1); - const cellUri2 = CellUri.generate(notebook.uri, 2); - - extHostNotebookDocuments.$acceptModelChanged(notebookUri, new SerializableObjectWithBuffers({ - versionId: notebook.apiNotebook.version + 1, - rawEvents: [{ - kind: NotebookCellsChangeType.ModelChange, - changes: [[0, 0, [{ - handle: 1, - uri: cellUri1, - source: ['Hello', 'World', 'Hello World!'], - eol: '\n', - language: 'test', - cellKind: CellKind.Code, - outputs: [], - }, { - handle: 2, - uri: cellUri2, - source: ['Hallo', 'Welt', 'Hallo Welt!'], - eol: '\n', - language: 'test', - cellKind: CellKind.Code, - outputs: [], - }]] - ] - }] - }), false); - - - assert.strictEqual(notebook.apiNotebook.cellCount, 1 + 2); // markdown and code - - let doc = new ExtHostNotebookConcatDocument(extHostNotebookDocuments, extHostDocuments, notebook.apiNotebook, undefined); - - assert.strictEqual(doc.contains(cellUri1), true); - assert.strictEqual(doc.contains(cellUri2), true); - assert.strictEqual(doc.contains(URI.parse('some://miss/path')), false); - }); - - test('location, position mapping', function () { - - extHostNotebookDocuments.$acceptModelChanged(notebookUri, new SerializableObjectWithBuffers({ - versionId: notebook.apiNotebook.version + 1, - rawEvents: [ - { - kind: NotebookCellsChangeType.ModelChange, - changes: [[0, 0, [{ - handle: 1, - uri: CellUri.generate(notebook.uri, 1), - source: ['Hello', 'World', 'Hello World!'], - eol: '\n', - language: 'test', - cellKind: CellKind.Code, - outputs: [], - }, { - handle: 2, - uri: CellUri.generate(notebook.uri, 2), - source: ['Hallo', 'Welt', 'Hallo Welt!'], - eol: '\n', - language: 'test', - cellKind: CellKind.Code, - outputs: [], - }]]] - } - ] - }), false); - - - assert.strictEqual(notebook.apiNotebook.cellCount, 1 + 2); // markdown and code - - let doc = new ExtHostNotebookConcatDocument(extHostNotebookDocuments, extHostDocuments, notebook.apiNotebook, undefined); - assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!'); - - assertLocation(doc, new Position(0, 0), new Location(notebook.apiNotebook.cellAt(0).document.uri, new Position(0, 0))); - assertLocation(doc, new Position(4, 0), new Location(notebook.apiNotebook.cellAt(1).document.uri, new Position(1, 0))); - assertLocation(doc, new Position(4, 3), new Location(notebook.apiNotebook.cellAt(1).document.uri, new Position(1, 3))); - assertLocation(doc, new Position(5, 11), new Location(notebook.apiNotebook.cellAt(1).document.uri, new Position(2, 11))); - assertLocation(doc, new Position(5, 12), new Location(notebook.apiNotebook.cellAt(1).document.uri, new Position(2, 11)), false); // don't check identity because position will be clamped - }); - - - test('location, position mapping, cell changes', function () { - - let doc = new ExtHostNotebookConcatDocument(extHostNotebookDocuments, extHostDocuments, notebook.apiNotebook, undefined); - - // UPDATE 1 - extHostNotebookDocuments.$acceptModelChanged(notebookUri, new SerializableObjectWithBuffers({ - versionId: notebook.apiNotebook.version + 1, - rawEvents: [ - { - kind: NotebookCellsChangeType.ModelChange, - changes: [[0, 0, [{ - handle: 1, - uri: CellUri.generate(notebook.uri, 1), - source: ['Hello', 'World', 'Hello World!'], - eol: '\n', - language: 'test', - cellKind: CellKind.Code, - outputs: [], - }]]] - } - ] - }), false); - assert.strictEqual(notebook.apiNotebook.cellCount, 1 + 1); - assert.strictEqual(doc.version, 1); - assertLines(doc, 'Hello', 'World', 'Hello World!'); - - assertLocation(doc, new Position(0, 0), new Location(notebook.apiNotebook.cellAt(0).document.uri, new Position(0, 0))); - assertLocation(doc, new Position(2, 2), new Location(notebook.apiNotebook.cellAt(0).document.uri, new Position(2, 2))); - assertLocation(doc, new Position(4, 0), new Location(notebook.apiNotebook.cellAt(0).document.uri, new Position(2, 12)), false); // clamped - - - // UPDATE 2 - extHostNotebookDocuments.$acceptModelChanged(notebookUri, new SerializableObjectWithBuffers({ - versionId: notebook.apiNotebook.version + 1, - rawEvents: [ - { - kind: NotebookCellsChangeType.ModelChange, - changes: [[1, 0, [{ - handle: 2, - uri: CellUri.generate(notebook.uri, 2), - source: ['Hallo', 'Welt', 'Hallo Welt!'], - eol: '\n', - language: 'test', - cellKind: CellKind.Code, - outputs: [], - }]]] - } - ] - }), false); - - assert.strictEqual(notebook.apiNotebook.cellCount, 1 + 2); - assert.strictEqual(doc.version, 2); - assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!'); - assertLocation(doc, new Position(0, 0), new Location(notebook.apiNotebook.cellAt(0).document.uri, new Position(0, 0))); - assertLocation(doc, new Position(4, 0), new Location(notebook.apiNotebook.cellAt(1).document.uri, new Position(1, 0))); - assertLocation(doc, new Position(4, 3), new Location(notebook.apiNotebook.cellAt(1).document.uri, new Position(1, 3))); - assertLocation(doc, new Position(5, 11), new Location(notebook.apiNotebook.cellAt(1).document.uri, new Position(2, 11))); - assertLocation(doc, new Position(5, 12), new Location(notebook.apiNotebook.cellAt(1).document.uri, new Position(2, 11)), false); // don't check identity because position will be clamped - - // UPDATE 3 (remove cell #2 again) - extHostNotebookDocuments.$acceptModelChanged(notebookUri, new SerializableObjectWithBuffers({ - versionId: notebook.apiNotebook.version + 1, - rawEvents: [ - { - kind: NotebookCellsChangeType.ModelChange, - changes: [[1, 1, []]] - } - ] - }), false); - assert.strictEqual(notebook.apiNotebook.cellCount, 1 + 1); - assert.strictEqual(doc.version, 3); - assertLines(doc, 'Hello', 'World', 'Hello World!'); - assertLocation(doc, new Position(0, 0), new Location(notebook.apiNotebook.cellAt(0).document.uri, new Position(0, 0))); - assertLocation(doc, new Position(2, 2), new Location(notebook.apiNotebook.cellAt(0).document.uri, new Position(2, 2))); - assertLocation(doc, new Position(4, 0), new Location(notebook.apiNotebook.cellAt(0).document.uri, new Position(2, 12)), false); // clamped - }); - - test('location, position mapping, cell-document changes', function () { - - let doc = new ExtHostNotebookConcatDocument(extHostNotebookDocuments, extHostDocuments, notebook.apiNotebook, undefined); - - // UPDATE 1 - extHostNotebookDocuments.$acceptModelChanged(notebookUri, new SerializableObjectWithBuffers({ - versionId: notebook.apiNotebook.version + 1, - rawEvents: [ - { - - kind: NotebookCellsChangeType.ModelChange, - changes: [[0, 0, [{ - handle: 1, - uri: CellUri.generate(notebook.uri, 1), - source: ['Hello', 'World', 'Hello World!'], - eol: '\n', - language: 'test', - cellKind: CellKind.Code, - outputs: [], - }, { - handle: 2, - uri: CellUri.generate(notebook.uri, 2), - source: ['Hallo', 'Welt', 'Hallo Welt!'], - eol: '\n', - language: 'test', - cellKind: CellKind.Code, - outputs: [], - }]]] - } - ] - }), false); - assert.strictEqual(notebook.apiNotebook.cellCount, 1 + 2); - assert.strictEqual(doc.version, 1); - - assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!'); - assertLocation(doc, new Position(0, 0), new Location(notebook.apiNotebook.cellAt(0).document.uri, new Position(0, 0))); - assertLocation(doc, new Position(2, 2), new Location(notebook.apiNotebook.cellAt(0).document.uri, new Position(2, 2))); - assertLocation(doc, new Position(2, 12), new Location(notebook.apiNotebook.cellAt(0).document.uri, new Position(2, 12))); - assertLocation(doc, new Position(4, 0), new Location(notebook.apiNotebook.cellAt(1).document.uri, new Position(1, 0))); - assertLocation(doc, new Position(4, 3), new Location(notebook.apiNotebook.cellAt(1).document.uri, new Position(1, 3))); - - // offset math - let cell1End = doc.offsetAt(new Position(2, 12)); - assert.strictEqual(doc.positionAt(cell1End).isEqual(new Position(2, 12)), true); - - extHostDocuments.$acceptModelChanged(notebook.apiNotebook.cellAt(0).document.uri, { - versionId: 0, - eol: '\n', - changes: [{ - range: { startLineNumber: 3, startColumn: 1, endLineNumber: 3, endColumn: 6 }, - rangeLength: 6, - rangeOffset: 12, - text: 'Hi' - }], - isRedoing: false, - isUndoing: false, - }, false); - assertLines(doc, 'Hello', 'World', 'Hi World!', 'Hallo', 'Welt', 'Hallo Welt!'); - assertLocation(doc, new Position(2, 12), new Location(notebook.apiNotebook.cellAt(0).document.uri, new Position(2, 9)), false); - - assert.strictEqual(doc.positionAt(cell1End).isEqual(new Position(3, 2)), true); - - }); - - test('selector', function () { - - extHostNotebookDocuments.$acceptModelChanged(notebookUri, new SerializableObjectWithBuffers({ - versionId: notebook.apiNotebook.version + 1, - rawEvents: [ - { - kind: NotebookCellsChangeType.ModelChange, - changes: [[0, 0, [{ - handle: 1, - uri: CellUri.generate(notebook.uri, 1), - source: ['fooLang-document'], - eol: '\n', - language: 'fooLang', - cellKind: CellKind.Code, - outputs: [], - }, { - handle: 2, - uri: CellUri.generate(notebook.uri, 2), - source: ['barLang-document'], - eol: '\n', - language: 'barLang', - cellKind: CellKind.Code, - outputs: [], - }]]] - } - ] - }), false); - - const mixedDoc = new ExtHostNotebookConcatDocument(extHostNotebookDocuments, extHostDocuments, notebook.apiNotebook, undefined); - const fooLangDoc = new ExtHostNotebookConcatDocument(extHostNotebookDocuments, extHostDocuments, notebook.apiNotebook, 'fooLang'); - const barLangDoc = new ExtHostNotebookConcatDocument(extHostNotebookDocuments, extHostDocuments, notebook.apiNotebook, 'barLang'); - - assertLines(mixedDoc, 'fooLang-document', 'barLang-document'); - assertLines(fooLangDoc, 'fooLang-document'); - assertLines(barLangDoc, 'barLang-document'); - - extHostNotebookDocuments.$acceptModelChanged(notebookUri, new SerializableObjectWithBuffers({ - versionId: notebook.apiNotebook.version + 1, - rawEvents: [ - { - kind: NotebookCellsChangeType.ModelChange, - changes: [[2, 0, [{ - handle: 3, - uri: CellUri.generate(notebook.uri, 3), - source: ['barLang-document2'], - eol: '\n', - language: 'barLang', - cellKind: CellKind.Code, - outputs: [], - }]]] - } - ] - }), false); - - assertLines(mixedDoc, 'fooLang-document', 'barLang-document', 'barLang-document2'); - assertLines(fooLangDoc, 'fooLang-document'); - assertLines(barLangDoc, 'barLang-document', 'barLang-document2'); - }); - - function assertOffsetAtPosition(doc: vscode.NotebookConcatTextDocument, offset: number, expected: { line: number; character: number }, reverse = true) { - const actual = doc.positionAt(offset); - - assert.strictEqual(actual.line, expected.line); - assert.strictEqual(actual.character, expected.character); - - if (reverse) { - const actualOffset = doc.offsetAt(actual); - assert.strictEqual(actualOffset, offset); - } - } - - - test('offsetAt(position) <-> positionAt(offset)', function () { - - extHostNotebookDocuments.$acceptModelChanged(notebookUri, new SerializableObjectWithBuffers({ - versionId: notebook.apiNotebook.version + 1, - rawEvents: [ - { - kind: NotebookCellsChangeType.ModelChange, - changes: [[0, 0, [{ - handle: 1, - uri: CellUri.generate(notebook.uri, 1), - source: ['Hello', 'World', 'Hello World!'], - eol: '\n', - language: 'test', - cellKind: CellKind.Code, - outputs: [], - }, { - handle: 2, - uri: CellUri.generate(notebook.uri, 2), - source: ['Hallo', 'Welt', 'Hallo Welt!'], - eol: '\n', - language: 'test', - cellKind: CellKind.Code, - outputs: [], - }]]] - } - ] - }), false); - - assert.strictEqual(notebook.apiNotebook.cellCount, 1 + 2); // markdown and code - - let doc = new ExtHostNotebookConcatDocument(extHostNotebookDocuments, extHostDocuments, notebook.apiNotebook, undefined); - assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!'); - - assertOffsetAtPosition(doc, 0, { line: 0, character: 0 }); - assertOffsetAtPosition(doc, 1, { line: 0, character: 1 }); - assertOffsetAtPosition(doc, 9, { line: 1, character: 3 }); - assertOffsetAtPosition(doc, 32, { line: 4, character: 1 }); - assertOffsetAtPosition(doc, 47, { line: 5, character: 11 }); - }); - - - function assertLocationAtPosition(doc: vscode.NotebookConcatTextDocument, pos: { line: number; character: number }, expected: { uri: URI; line: number; character: number }, reverse = true) { - - const actual = doc.locationAt(new Position(pos.line, pos.character)); - assert.strictEqual(actual.uri.toString(), expected.uri.toString()); - assert.strictEqual(actual.range.start.line, expected.line); - assert.strictEqual(actual.range.end.line, expected.line); - assert.strictEqual(actual.range.start.character, expected.character); - assert.strictEqual(actual.range.end.character, expected.character); - - if (reverse) { - const actualPos = doc.positionAt(actual); - assert.strictEqual(actualPos.line, pos.line); - assert.strictEqual(actualPos.character, pos.character); - } - } - - test('locationAt(position) <-> positionAt(location)', function () { - - extHostNotebookDocuments.$acceptModelChanged(notebookUri, new SerializableObjectWithBuffers({ - versionId: notebook.apiNotebook.version + 1, - rawEvents: [ - { - kind: NotebookCellsChangeType.ModelChange, - changes: [[0, 0, [{ - handle: 1, - uri: CellUri.generate(notebook.uri, 1), - source: ['Hello', 'World', 'Hello World!'], - eol: '\n', - language: 'test', - cellKind: CellKind.Code, - outputs: [], - }, { - handle: 2, - uri: CellUri.generate(notebook.uri, 2), - source: ['Hallo', 'Welt', 'Hallo Welt!'], - eol: '\n', - language: 'test', - cellKind: CellKind.Code, - outputs: [], - }]]] - } - ] - }), false); - - assert.strictEqual(notebook.apiNotebook.cellCount, 1 + 2); // markdown and code - - let doc = new ExtHostNotebookConcatDocument(extHostNotebookDocuments, extHostDocuments, notebook.apiNotebook, undefined); - assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!'); - - assertLocationAtPosition(doc, { line: 0, character: 0 }, { uri: notebook.apiNotebook.cellAt(0).document.uri, line: 0, character: 0 }); - assertLocationAtPosition(doc, { line: 2, character: 0 }, { uri: notebook.apiNotebook.cellAt(0).document.uri, line: 2, character: 0 }); - assertLocationAtPosition(doc, { line: 2, character: 12 }, { uri: notebook.apiNotebook.cellAt(0).document.uri, line: 2, character: 12 }); - assertLocationAtPosition(doc, { line: 3, character: 0 }, { uri: notebook.apiNotebook.cellAt(1).document.uri, line: 0, character: 0 }); - assertLocationAtPosition(doc, { line: 5, character: 0 }, { uri: notebook.apiNotebook.cellAt(1).document.uri, line: 2, character: 0 }); - assertLocationAtPosition(doc, { line: 5, character: 11 }, { uri: notebook.apiNotebook.cellAt(1).document.uri, line: 2, character: 11 }); - }); - - test('getText(range)', function () { - - extHostNotebookDocuments.$acceptModelChanged(notebookUri, new SerializableObjectWithBuffers({ - versionId: notebook.apiNotebook.version + 1, - rawEvents: [ - { - kind: NotebookCellsChangeType.ModelChange, - changes: [[0, 0, [{ - handle: 1, - uri: CellUri.generate(notebook.uri, 1), - source: ['Hello', 'World', 'Hello World!'], - eol: '\n', - language: 'test', - cellKind: CellKind.Code, - outputs: [], - }, { - handle: 2, - uri: CellUri.generate(notebook.uri, 2), - source: ['Hallo', 'Welt', 'Hallo Welt!'], - eol: '\n', - language: 'test', - cellKind: CellKind.Code, - outputs: [], - }, { - handle: 3, - uri: CellUri.generate(notebook.uri, 3), - source: ['Three', 'Drei', 'Drüü'], - eol: '\n', - language: 'test', - cellKind: CellKind.Code, - outputs: [], - }]]] - } - ] - }), false); - - assert.strictEqual(notebook.apiNotebook.cellCount, 1 + 3); // markdown and code - - let doc = new ExtHostNotebookConcatDocument(extHostNotebookDocuments, extHostDocuments, notebook.apiNotebook, undefined); - assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!', 'Three', 'Drei', 'Drüü'); - - assert.strictEqual(doc.getText(new Range(0, 0, 0, 0)), ''); - assert.strictEqual(doc.getText(new Range(0, 0, 1, 0)), 'Hello\n'); - assert.strictEqual(doc.getText(new Range(2, 0, 4, 0)), 'Hello World!\nHallo\n'); - assert.strictEqual(doc.getText(new Range(2, 0, 8, 0)), 'Hello World!\nHallo\nWelt\nHallo Welt!\nThree\nDrei\n'); - }); - - test('validateRange/Position', function () { - - extHostNotebookDocuments.$acceptModelChanged(notebookUri, new SerializableObjectWithBuffers({ - versionId: notebook.apiNotebook.version + 1, - rawEvents: [ - { - kind: NotebookCellsChangeType.ModelChange, - changes: [[0, 0, [{ - handle: 1, - uri: CellUri.generate(notebook.uri, 1), - source: ['Hello', 'World', 'Hello World!'], - eol: '\n', - language: 'test', - cellKind: CellKind.Code, - outputs: [], - }, { - handle: 2, - uri: CellUri.generate(notebook.uri, 2), - source: ['Hallo', 'Welt', 'Hallo Welt!'], - eol: '\n', - language: 'test', - cellKind: CellKind.Code, - outputs: [], - }]]] - } - ] - }), false); - - assert.strictEqual(notebook.apiNotebook.cellCount, 1 + 2); // markdown and code - - let doc = new ExtHostNotebookConcatDocument(extHostNotebookDocuments, extHostDocuments, notebook.apiNotebook, undefined); - assertLines(doc, 'Hello', 'World', 'Hello World!', 'Hallo', 'Welt', 'Hallo Welt!'); - - - function assertPosition(actual: vscode.Position, expectedLine: number, expectedCh: number) { - assert.strictEqual(actual.line, expectedLine); - assert.strictEqual(actual.character, expectedCh); - } - - - // "fixed" - assertPosition(doc.validatePosition(new Position(0, 1000)), 0, 5); - assertPosition(doc.validatePosition(new Position(2, 1000)), 2, 12); - assertPosition(doc.validatePosition(new Position(5, 1000)), 5, 11); - assertPosition(doc.validatePosition(new Position(5000, 1000)), 5, 11); - - // "good" - assertPosition(doc.validatePosition(new Position(0, 1)), 0, 1); - assertPosition(doc.validatePosition(new Position(0, 5)), 0, 5); - assertPosition(doc.validatePosition(new Position(2, 8)), 2, 8); - assertPosition(doc.validatePosition(new Position(2, 12)), 2, 12); - assertPosition(doc.validatePosition(new Position(5, 11)), 5, 11); - - }); -}); diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 4bb2426b9a3..04e760d97ce 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -30,7 +30,6 @@ export const allApiProposals = Object.freeze({ inputBoxSeverity: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.inputBoxSeverity.d.ts', ipc: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.ipc.d.ts', notebookCellExecutionState: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookCellExecutionState.d.ts', - notebookConcatTextDocument: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookConcatTextDocument.d.ts', notebookContentProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookContentProvider.d.ts', notebookControllerKind: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookControllerKind.d.ts', notebookDebugOptions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookDebugOptions.d.ts', diff --git a/src/vscode-dts/vscode.proposed.notebookConcatTextDocument.d.ts b/src/vscode-dts/vscode.proposed.notebookConcatTextDocument.d.ts deleted file mode 100644 index 259708cbe8e..00000000000 --- a/src/vscode-dts/vscode.proposed.notebookConcatTextDocument.d.ts +++ /dev/null @@ -1,50 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module 'vscode' { - - // https://github.com/microsoft/vscode/issues/106744 - - export namespace notebooks { - /** - * @deprecated - */ - // todo@API really needed? we didn't find a user here - export function createConcatTextDocument(notebook: NotebookDocument, selector?: DocumentSelector): NotebookConcatTextDocument; - } - - /** @deprecated */ - export interface NotebookConcatTextDocument { - /** @deprecated */ - readonly uri: Uri; - /** @deprecated */ - readonly isClosed: boolean; - /** @deprecated */ - dispose(): void; - /** @deprecated */ - readonly onDidChange: Event; - /** @deprecated */ - readonly version: number; - /** @deprecated */ - getText(): string; - /** @deprecated */ - getText(range: Range): string; - - offsetAt(position: Position): number; - /** @deprecated */ - positionAt(offset: number): Position; - /** @deprecated */ - validateRange(range: Range): Range; - /** @deprecated */ - validatePosition(position: Position): Position; - - /** @deprecated */ - locationAt(positionOrRange: Position | Range): Location; - /** @deprecated */ - positionAt(location: Location): Position; - /** @deprecated */ - contains(uri: Uri): boolean; - } -} From cba0a0e4608f22b6af1e4bcd4588b47976b703fc Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 14 Apr 2022 14:55:22 -0700 Subject: [PATCH 104/245] Always encode paths dropped in md --- .../src/languageFeatures/dropIntoEditor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts b/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts index c87c2c7c67a..7e909f874de 100644 --- a/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts +++ b/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts @@ -52,7 +52,7 @@ export function registerDropIntoEditor(selector: vscode.DocumentSelector) { const snippet = new vscode.SnippetString(); uris.forEach((uri, i) => { const mdPath = document.uri.scheme === uri.scheme - ? path.relative(URI.Utils.dirname(document.uri).fsPath, uri.fsPath) + ? encodeURIComponent(path.relative(URI.Utils.dirname(document.uri).fsPath, uri.fsPath)) : uri.toString(false); const ext = URI.Utils.extname(uri).toLowerCase(); From 52a394bdeaf2efd827f46d0b0bb5efa23ed7c8d4 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Thu, 14 Apr 2022 15:21:45 -0700 Subject: [PATCH 105/245] Introduce removeSessionByIToken to remove sessions that aren't in this._tokens yet. Fixes #145189 --- .../microsoft-authentication/src/AADHelper.ts | 67 ++++++++++--------- .../microsoft-authentication/src/extension.ts | 2 +- 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/extensions/microsoft-authentication/src/AADHelper.ts b/extensions/microsoft-authentication/src/AADHelper.ts index 6cd4d0f684b..6d02e2472b7 100644 --- a/extensions/microsoft-authentication/src/AADHelper.ts +++ b/extensions/microsoft-authentication/src/AADHelper.ts @@ -40,18 +40,6 @@ interface IToken { sessionId: string; // The account id + the scope } -interface ITokenClaims { - tid: string; - email?: string; - unique_name?: string; - exp?: number; - preferred_username?: string; - oid?: string; - altsecid?: string; - ipd?: string; - scp: string; -} - interface IStoredSession { id: string; refreshToken: string; @@ -153,7 +141,16 @@ export class AzureActiveDirectoryService { } else { vscode.window.showErrorMessage(localize('signOut', "You have been signed out because reading stored authentication information failed.")); Logger.error(e); - await this.removeSession(session.id); + await this.removeSessionByIToken({ + accessToken: undefined, + refreshToken: session.refreshToken, + account: { + label: session.account.label ?? session.account.displayName!, + id: session.account.id + }, + scope: session.scope, + sessionId: session.id + }); } } }); @@ -386,27 +383,16 @@ export class AzureActiveDirectoryService { }); } - public async removeSession(sessionId: string, writeToDisk: boolean = true): Promise { + public removeSessionById(sessionId: string, writeToDisk: boolean = true): Promise { Logger.info(`Logging out of session '${sessionId}'`); const tokenIndex = this._tokens.findIndex(token => token.sessionId === sessionId); if (tokenIndex === -1) { Logger.info(`Session not found '${sessionId}'`); - return undefined; + return Promise.resolve(undefined); } - const token = this._tokens[tokenIndex]; - this._tokens.splice(tokenIndex, 1); - this.removeSessionTimeout(sessionId); - - if (writeToDisk) { - await this._tokenStorage.delete(sessionId); - } - - const session = this.convertToSessionSync(token); - Logger.info(`Sending change event for session that was removed with scopes: ${token.scope}`); - onDidChangeSessions.fire({ added: [], removed: [session], changed: [] }); - Logger.info(`Logged out of session '${sessionId}' with scopes: ${token.scope}`); - return session; + const token = this._tokens.splice(tokenIndex, 1)[0]; + return this.removeSessionByIToken(token, writeToDisk); } public async clearSessions() { @@ -421,6 +407,25 @@ export class AzureActiveDirectoryService { this._refreshTimeouts.clear(); } + private async removeSessionByIToken(token: IToken, writeToDisk: boolean = true): Promise { + this.removeSessionTimeout(token.sessionId); + + if (writeToDisk) { + await this._tokenStorage.delete(token.sessionId); + } + + const tokenIndex = this._tokens.findIndex(t => t.sessionId === token.sessionId); + if (tokenIndex !== -1) { + this._tokens.splice(tokenIndex, 1); + } + + const session = this.convertToSessionSync(token); + Logger.info(`Sending change event for session that was removed with scopes: ${token.scope}`); + onDidChangeSessions.fire({ added: [], removed: [session], changed: [] }); + Logger.info(`Logged out of session '${token.sessionId}' with scopes: ${token.scope}`); + return session; + } + //#endregion //#region timeout @@ -435,7 +440,7 @@ export class AzureActiveDirectoryService { } catch (e) { if (e.message !== REFRESH_NETWORK_FAILURE) { vscode.window.showErrorMessage(localize('signOut', "You have been signed out because reading stored authentication information failed.")); - await this.removeSession(sessionId); + await this.removeSessionById(sessionId); } } }, timeout)); @@ -792,7 +797,7 @@ export class AzureActiveDirectoryService { // Network failures will automatically retry on next poll. if (e.message !== REFRESH_NETWORK_FAILURE) { vscode.window.showErrorMessage(localize('signOut', "You have been signed out because reading stored authentication information failed.")); - await this.removeSession(session.id); + await this.removeSessionById(session.id); } return; } @@ -801,7 +806,7 @@ export class AzureActiveDirectoryService { for (const { value } of e.removed) { Logger.info(`Session removed in another window with scopes: ${value.scope}`); - const session = await this.removeSession(value.id, false); + const session = await this.removeSessionById(value.id, false); if (session) { removed.push(session); } diff --git a/extensions/microsoft-authentication/src/extension.ts b/extensions/microsoft-authentication/src/extension.ts index f43467f2b91..06a2facdf13 100644 --- a/extensions/microsoft-authentication/src/extension.ts +++ b/extensions/microsoft-authentication/src/extension.ts @@ -48,7 +48,7 @@ export async function activate(context: vscode.ExtensionContext) { */ telemetryReporter.sendTelemetryEvent('logout'); - const session = await loginService.removeSession(id); + const session = await loginService.removeSessionById(id); if (session) { onDidChangeSessions.fire({ added: [], removed: [session], changed: [] }); } From b55d829608115a26fff5f13339fc0310f82e4441 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Thu, 14 Apr 2022 15:30:38 -0700 Subject: [PATCH 106/245] remove unneeded cast --- .../welcomeGettingStarted/browser/gettingStartedService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService.ts index 75b3891db8d..5fe7dd326e7 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService.ts @@ -340,7 +340,7 @@ export class WalkthroughsService extends Disposable implements IWalkthroughsServ } if (step.media.image) { - const altText = (step.media as any).altText; + const altText = step.media.altText; if (altText === undefined) { console.error('Walkthrough item:', fullyQualifiedID, 'is missing altText for its media element.'); } From 49d2821d445fa186312fee0635f7cc13f22cf097 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Thu, 14 Apr 2022 15:31:27 -0700 Subject: [PATCH 107/245] add note about legacy config --- .../welcomeGettingStarted/browser/gettingStartedService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService.ts index 5fe7dd326e7..4be26d8aedc 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService.ts @@ -362,7 +362,7 @@ export class WalkthroughsService extends Disposable implements IWalkthroughsServ }; } - // Legacy media config + // Legacy media config (only in use by remote-wsl at the moment) else { const legacyMedia = step.media as unknown as { path: string; altText: string }; if (typeof legacyMedia.path === 'string' && legacyMedia.path.endsWith('.md')) { From a9288be67bab4e9c4dbe62207f01fafc2bdaadcb Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 14 Apr 2022 17:41:16 -0700 Subject: [PATCH 108/245] Focus the editor on debug stop, by default. Fix #139950 --- src/vs/workbench/contrib/debug/browser/debug.contribution.ts | 5 +++++ src/vs/workbench/contrib/debug/browser/debugSession.ts | 3 ++- src/vs/workbench/contrib/debug/common/debug.ts | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 03a03efeb2a..2cdb553512b 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -511,6 +511,11 @@ configurationRegistry.registerConfiguration({ description: nls.localize('debug.focusWindowOnBreak', "Controls whether the workbench window should be focused when the debugger breaks."), default: true }, + 'debug.focusEditorOnBreak': { + type: 'boolean', + description: nls.localize('debug.focusEditorOnBreak', "Controls whether the editor should be focused when the debugger breaks."), + default: true + }, 'debug.onTaskErrors': { enum: ['debugAnyway', 'showErrors', 'prompt', 'abort'], enumDescriptions: [nls.localize('debugAnyway', "Ignore task errors and start debugging."), nls.localize('showErrors', "Show the Problems view and do not start debugging."), nls.localize('prompt', "Prompt user."), nls.localize('cancel', "Cancel debugging.")], diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 0e8671611e0..8e119b5e5a3 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -958,7 +958,8 @@ export class DebugSession implements IDebugSession { const focusedStackFrame = this.debugService.getViewModel().focusedStackFrame; if (!focusedStackFrame || focusedStackFrame.thread.session === this) { // Only take focus if nothing is focused, or if the focus is already on the current session - await this.debugService.focusStackFrame(undefined, thread); + const preserveFocus = !this.configurationService.getValue('debug').focusEditorOnBreak; + await this.debugService.focusStackFrame(undefined, thread, undefined, { preserveFocus }); } if (thread.stoppedDetails) { diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index fb0445b670e..aa5625fe460 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -630,6 +630,7 @@ export interface IDebugConfiguration { acceptSuggestionOnEnter: 'off' | 'on'; }; focusWindowOnBreak: boolean; + focusEditorOnBreak: boolean; onTaskErrors: 'debugAnyway' | 'showErrors' | 'prompt' | 'abort'; showBreakpointsInOverviewRuler: boolean; showInlineBreakpointCandidates: boolean; From 4f03e1bc7f384149e71c8ee843101a2fadc959e8 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Fri, 15 Apr 2022 10:18:42 -0400 Subject: [PATCH 109/245] Up distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f3462182339..2866cc2fd68 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.67.0", - "distro": "142f9ce10fa53266171369134b31bd1f222b826b", + "distro": "0db75775b5bb0ca7b9f311350da4a3c1913cb4f7", "author": { "name": "Microsoft Corporation" }, From 2e10985e624c525dc2f77c9ce73456adcd047ba2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 15 Apr 2022 16:28:04 +0200 Subject: [PATCH 110/245] quick pick - hide input in more cases of quick nav (#147493) --- src/vs/base/parts/quickinput/browser/quickInput.ts | 1 + src/vs/base/parts/quickinput/common/quickInput.ts | 7 +++++++ src/vs/workbench/browser/actions/windowActions.ts | 1 + src/vs/workbench/electron-sandbox/actions/windowActions.ts | 1 + 4 files changed, 10 insertions(+) diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts index 9eb6fa7e574..0dca8a2bd38 100644 --- a/src/vs/base/parts/quickinput/browser/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -1477,6 +1477,7 @@ export class QuickInputController extends Disposable { input.matchOnLabel = (options.matchOnLabel === undefined) || options.matchOnLabel; // default to true input.autoFocusOnList = (options.autoFocusOnList === undefined) || options.autoFocusOnList; // default to true input.quickNavigate = options.quickNavigate; + input.hideInput = !!options.hideInput; input.contextKey = options.contextKey; input.busy = true; Promise.all([picks, options.activeItem]) diff --git a/src/vs/base/parts/quickinput/common/quickInput.ts b/src/vs/base/parts/quickinput/common/quickInput.ts index 23e42343991..bf9979e00b1 100644 --- a/src/vs/base/parts/quickinput/common/quickInput.ts +++ b/src/vs/base/parts/quickinput/common/quickInput.ts @@ -104,6 +104,13 @@ export interface IPickOptions { */ quickNavigate?: IQuickNavigateConfiguration; + /** + * Hides the input box from the picker UI. This is typically used + * in combination with quick-navigation where no search UI should + * be presented. + */ + hideInput?: boolean; + /** * a context key to set when this picker is active */ diff --git a/src/vs/workbench/browser/actions/windowActions.ts b/src/vs/workbench/browser/actions/windowActions.ts index 748ababc6db..a0d14adf991 100644 --- a/src/vs/workbench/browser/actions/windowActions.ts +++ b/src/vs/workbench/browser/actions/windowActions.ts @@ -145,6 +145,7 @@ abstract class BaseOpenRecentAction extends Action2 { matchOnDescription: true, onKeyMods: mods => keyMods = mods, quickNavigate: this.isQuickNavigate() ? { keybindings: keybindingService.lookupKeybindings(this.desc.id) } : undefined, + hideInput: this.isQuickNavigate(), onDidTriggerItemButton: async context => { // Remove diff --git a/src/vs/workbench/electron-sandbox/actions/windowActions.ts b/src/vs/workbench/electron-sandbox/actions/windowActions.ts index 8ab7286bcfe..a89681fa292 100644 --- a/src/vs/workbench/electron-sandbox/actions/windowActions.ts +++ b/src/vs/workbench/electron-sandbox/actions/windowActions.ts @@ -227,6 +227,7 @@ abstract class BaseSwitchWindow extends Action2 { activeItem: picks[autoFocusIndex], placeHolder, quickNavigate: this.isQuickNavigate() ? { keybindings: keybindingService.lookupKeybindings(this.desc.id) } : undefined, + hideInput: this.isQuickNavigate(), onDidTriggerItemButton: async context => { await nativeHostService.closeWindowById(context.item.payload); context.removeItem(); From ff289ebac8c29c304c84d997ac181aada522eecc Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 15 Apr 2022 11:29:21 -0700 Subject: [PATCH 111/245] Support 'when' clause for breakpoints contribution. Fix #133585 --- .../debug/browser/debugAdapterManager.ts | 16 +++++------ .../contrib/debug/common/breakpoints.ts | 27 +++++++++++++++++++ .../workbench/contrib/debug/common/debug.ts | 5 ++++ .../contrib/debug/common/debugSchemas.ts | 15 ++++++----- 4 files changed, 46 insertions(+), 17 deletions(-) create mode 100644 src/vs/workbench/contrib/debug/common/breakpoints.ts diff --git a/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts b/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts index 2a01435a6f5..24c64bc9f81 100644 --- a/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts @@ -10,8 +10,8 @@ import Severity from 'vs/base/common/severity'; import * as strings from 'vs/base/common/strings'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorModel } from 'vs/editor/common/editorCommon'; -import { ITextModel } from 'vs/editor/common/model'; import { ILanguageService } from 'vs/editor/common/languages/language'; +import { ITextModel } from 'vs/editor/common/model'; import * as nls from 'vs/nls'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -22,6 +22,7 @@ import { Extensions as JSONExtensions, IJSONContributionRegistry } from 'vs/plat import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { Breakpoints } from 'vs/workbench/contrib/debug/common/breakpoints'; import { CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_EXTENSION_AVAILABLE, IAdapterDescriptor, IAdapterManager, IConfig, IDebugAdapter, IDebugAdapterDescriptorFactory, IDebugAdapterFactory, IDebugConfiguration, IDebugSession, INTERNAL_CONSOLE_OPTIONS_SCHEMA } from 'vs/workbench/contrib/debug/common/debug'; import { Debugger } from 'vs/workbench/contrib/debug/common/debugger'; import { breakpointsExtPoint, debuggersExtPoint, launchSchema, presentationSchema } from 'vs/workbench/contrib/debug/common/debugSchemas'; @@ -42,7 +43,7 @@ export class AdapterManager extends Disposable implements IAdapterManager { private debugExtensionsAvailable: IContextKey; private readonly _onDidRegisterDebugger = new Emitter(); private readonly _onDidDebuggersExtPointRead = new Emitter(); - private breakpointLanguageIdsSet = new Set(); + private breakpointContributions: Breakpoints[] = []; private debuggerWhenKeys = new Set(); constructor( @@ -116,13 +117,8 @@ export class AdapterManager extends Disposable implements IAdapterManager { this._onDidDebuggersExtPointRead.fire(); }); - breakpointsExtPoint.setHandler((extensions, delta) => { - delta.removed.forEach(removed => { - removed.value.forEach(breakpoints => this.breakpointLanguageIdsSet.delete(breakpoints.language)); - }); - delta.added.forEach(added => { - added.value.forEach(breakpoints => this.breakpointLanguageIdsSet.add(breakpoints.language)); - }); + breakpointsExtPoint.setHandler(extensions => { + this.breakpointContributions = extensions.flatMap(ext => ext.value.map(breakpoint => this.instantiationService.createInstance(Breakpoints, breakpoint))); }); } @@ -281,7 +277,7 @@ export class AdapterManager extends Disposable implements IAdapterManager { return true; } - return this.breakpointLanguageIdsSet.has(languageId); + return this.breakpointContributions.some(breakpoints => breakpoints.language === languageId && breakpoints.enabled); } getDebugger(type: string): Debugger | undefined { diff --git a/src/vs/workbench/contrib/debug/common/breakpoints.ts b/src/vs/workbench/contrib/debug/common/breakpoints.ts new file mode 100644 index 00000000000..38bc0d5114d --- /dev/null +++ b/src/vs/workbench/contrib/debug/common/breakpoints.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 { ContextKeyExpr, ContextKeyExpression, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IBreakpointContribution } from 'vs/workbench/contrib/debug/common/debug'; + +export class Breakpoints { + + private breakpointsWhen: ContextKeyExpression | undefined; + + constructor( + private readonly breakpointContribution: IBreakpointContribution, + @IContextKeyService private readonly contextKeyService: IContextKeyService, + ) { + this.breakpointsWhen = typeof breakpointContribution.when === 'string' ? ContextKeyExpr.deserialize(breakpointContribution.when) : undefined; + } + + get language(): string { + return this.breakpointContribution.language; + } + + get enabled(): boolean { + return !this.breakpointsWhen || this.contextKeyService.contextMatchesRules(this.breakpointsWhen); + } +} diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index aa5625fe460..1ea78541aff 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -772,6 +772,11 @@ export interface IDebuggerContribution extends IPlatformSpecificAdapterContribut when?: string; } +export interface IBreakpointContribution { + language: string; + when?: string; +} + export enum DebugConfigurationProviderTriggerKind { /** * `DebugConfigurationProvider.provideDebugConfigurations` is called to provide the initial debug configurations for a newly created launch.json. diff --git a/src/vs/workbench/contrib/debug/common/debugSchemas.ts b/src/vs/workbench/contrib/debug/common/debugSchemas.ts index 3613a1b44a4..a2a9c7f5480 100644 --- a/src/vs/workbench/contrib/debug/common/debugSchemas.ts +++ b/src/vs/workbench/contrib/debug/common/debugSchemas.ts @@ -5,7 +5,7 @@ import * as extensionsRegistry from 'vs/workbench/services/extensions/common/extensionsRegistry'; import * as nls from 'vs/nls'; -import { IDebuggerContribution, ICompound } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebuggerContribution, ICompound, IBreakpointContribution } from 'vs/workbench/contrib/debug/common/debug'; import { launchSchemaId } from 'vs/workbench/services/configuration/common/configuration'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { inputsSchema } from 'vs/workbench/services/configurationResolver/common/configurationResolverSchema'; @@ -68,7 +68,7 @@ export const debuggersExtPoint = extensionsRegistry.ExtensionsRegistry.registerE type: 'object' }, when: { - description: nls.localize('vscode.extension.contributes.debuggers.when', "Condition which must be true to enable this type of debugger. Consider using 'shellExecutionSupported', 'virtualWorkspace', 'resourceScheme' or an extension defined context key as appropriate for this."), + description: nls.localize('vscode.extension.contributes.debuggers.when', "Condition which must be true to enable this type of debugger. Consider using 'shellExecutionSupported', 'virtualWorkspace', 'resourceScheme' or an extension-defined context key as appropriate for this."), type: 'string', default: '' }, @@ -107,12 +107,8 @@ export const debuggersExtPoint = extensionsRegistry.ExtensionsRegistry.registerE } }); -export interface IRawBreakpointContribution { - language: string; -} - // breakpoints extension point #9037 -export const breakpointsExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint({ +export const breakpointsExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'breakpoints', jsonSchema: { description: nls.localize('vscode.extension.contributes.breakpoints', 'Contributes breakpoints.'), @@ -127,6 +123,11 @@ export const breakpointsExtPoint = extensionsRegistry.ExtensionsRegistry.registe description: nls.localize('vscode.extension.contributes.breakpoints.language', "Allow breakpoints for this language."), type: 'string' }, + when: { + description: nls.localize('vscode.extension.contributes.breakpoints.when', "Condition which must be true to enable breakpoints in this language. Consider matching this to the debugger when clause as appropriate."), + type: 'string', + default: '' + } } } } From 2fd2a6041557ea4c7784b8ee7f785a114452a434 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 15 Apr 2022 11:55:55 -0700 Subject: [PATCH 112/245] Add autoExpandLazyVariables setting. Fix #144861 --- .../workbench/contrib/debug/browser/debug.contribution.ts | 5 +++++ src/vs/workbench/contrib/debug/browser/debugSession.ts | 5 +++++ src/vs/workbench/contrib/debug/common/debug.ts | 2 ++ src/vs/workbench/contrib/debug/common/debugModel.ts | 8 +++++++- 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 2cdb553512b..d595b7768d1 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -558,5 +558,10 @@ configurationRegistry.registerConfiguration({ default: true, description: nls.localize('debug.disassemblyView.showSourceCode', "Show Source Code in Disassembly View.") }, + 'debug.autoExpandLazyVariables': { + type: 'boolean', + default: false, + description: nls.localize('debug.autoExpandLazyVariables', "Automatically show values for variables that are lazily resolved by the debugger, such as getters.") + } } }); diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 8e119b5e5a3..3ff60d94d24 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -172,6 +172,11 @@ export class DebugSession implements IDebugSession { return this._options.debugUI?.simple ?? false; } + get autoExpandLazyVariables(): boolean { + // This tiny helper avoids converting the entire debug model to use service injection + return this.configurationService.getValue('debug').autoExpandLazyVariables; + } + setConfiguration(configuration: { resolved: IConfig; unresolved: IConfig | undefined }) { this._configuration = configuration; } diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 1ea78541aff..9457ae8c1b4 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -291,6 +291,7 @@ export interface IDebugSession extends ITreeElement { readonly compoundRoot: DebugCompoundRoot | undefined; readonly name: string; readonly isSimpleUI: boolean; + readonly autoExpandLazyVariables: boolean; setSubId(subId: string | undefined): void; @@ -638,6 +639,7 @@ export interface IDebugConfiguration { disassemblyView: { showSourceCode: boolean; }; + autoExpandLazyVariables: boolean; } export interface IGlobalConfig { diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 3e2cb2e1a16..c05fc04c956 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -152,7 +152,7 @@ export class ExpressionContainer implements IExpressionContainer { } const nameCount = new Map(); - return response.body.variables.filter(v => !!v).map((v: IDebugProtocolVariableWithContext) => { + const vars = response.body.variables.filter(v => !!v).map((v: IDebugProtocolVariableWithContext) => { if (isString(v.value) && isString(v.name) && typeof v.variablesReference === 'number') { const count = nameCount.get(v.name) || 0; const idDuplicationIndex = count > 0 ? count.toString() : ''; @@ -161,6 +161,12 @@ export class ExpressionContainer implements IExpressionContainer { } return new Variable(this.session, this.threadId, this, 0, '', undefined, nls.localize('invalidVariableAttributes', "Invalid variable attributes"), 0, 0, undefined, { kind: 'virtual' }, undefined, undefined, false); }); + + if (this.session!.autoExpandLazyVariables) { + await Promise.all(vars.map(v => v.presentationHint?.lazy && v.evaluateLazy())); + } + + return vars; } catch (e) { return [new Variable(this.session, this.threadId, this, 0, '', undefined, e.message, 0, 0, undefined, { kind: 'virtual' }, undefined, undefined, false)]; } From 1fb94816f2d40bdc77536eac44eada91a9a9e8ac Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Fri, 15 Apr 2022 13:35:40 -0700 Subject: [PATCH 113/245] Settings editor search widget polish (#147521) --- .../preferences/browser/settingsEditor2.ts | 9 +++--- .../preferences/browser/settingsSearchMenu.ts | 28 +++++++++---------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index d71c2e6acec..6ac2b721752 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -500,11 +500,11 @@ export class SettingsEditor2 extends EditorPane { clearSearchFilters(): void { let query = this.searchWidget.getValue(); - SettingsEditor2.SUGGESTIONS.forEach(suggestion => { - query = query.replace(suggestion, ''); + const splitQuery = query.split(' ').filter(word => { + return word.length && !SettingsEditor2.SUGGESTIONS.some(suggestion => word.startsWith(suggestion)); }); - this.searchWidget.setValue(query.trim()); + this.searchWidget.setValue(splitQuery.join(' ')); } private updateInputAriaLabel() { @@ -534,9 +534,10 @@ export class SettingsEditor2 extends EditorPane { // for the ':' trigger, only return suggestions if there was a '@' before it in the same word. const queryParts = query.split(/\s/g); if (queryParts[queryParts.length - 1].startsWith(`@${LANGUAGE_SETTING_TAG}`)) { - return this.languageService.getRegisteredLanguageIds().map(languageId => { + const sortedLanguages = this.languageService.getRegisteredLanguageIds().map(languageId => { return `@${LANGUAGE_SETTING_TAG}${languageId} `; }).sort(); + return sortedLanguages.filter(langFilter => !query.includes(langFilter)); } else if (queryParts[queryParts.length - 1].startsWith('@')) { return SettingsEditor2.SUGGESTIONS.filter(tag => !query.includes(tag)).map(tag => tag.endsWith(':') ? tag : tag + ' '); } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts b/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts index b8b09ebc8ff..83675b77e00 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts @@ -90,39 +90,39 @@ export class SettingsSearchFilterDropdownMenuActionViewItem extends DropdownMenu this.createModifiedAction(), this.createAction( 'extSettingsSearch', - localize('extSettingsSearch', "Extension ID"), + localize('extSettingsSearch', "Extension ID..."), localize('extSettingsSearchTooltip', "Add extension ID filter"), `@${EXTENSION_SETTING_TAG}`, false ), this.createAction( 'featuresSettingsSearch', - localize('featureSettingsSearch', "Feature"), + localize('featureSettingsSearch', "Feature..."), localize('featureSettingsSearchTooltip', "Add feature filter"), `@${FEATURE_SETTING_TAG}`, true ), this.createAction( - 'idSettingsSearch', - localize('idSettingsSearch', "Setting ID"), - localize('idSettingsSearchTooltip', "Add setting ID filter"), - `@${ID_SETTING_TAG}`, - false + 'tagSettingsSearch', + localize('tagSettingsSearch', "Tag..."), + localize('tagSettingsSearchTooltip', "Add tag filter"), + `@${GENERAL_TAG_SETTING_TAG}`, + true ), this.createAction( 'langSettingsSearch', - localize('langSettingsSearch', "Language"), + localize('langSettingsSearch', "Language..."), localize('langSettingsSearchTooltip', "Add language ID filter"), `@${LANGUAGE_SETTING_TAG}`, true ), this.createAction( - 'tagSettingsSearch', - localize('tagSettingsSearch', "Tag"), - localize('tagSettingsSearchTooltip', "Add tag filter"), - `@${GENERAL_TAG_SETTING_TAG}`, - true - ), + 'idSettingsSearch', + localize('idSettingsSearch', "Setting ID..."), + localize('idSettingsSearchTooltip', "Add setting ID filter"), + `@${ID_SETTING_TAG}`, + false + ) ]; } } From 70c896184e317984db6f0ff6d605f940b4c1a41f Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 15 Apr 2022 15:28:33 -0700 Subject: [PATCH 114/245] Fix build --- src/vs/workbench/contrib/debug/test/browser/mockDebug.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts b/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts index 81c394466ec..1bebaa31adb 100644 --- a/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts +++ b/src/vs/workbench/contrib/debug/test/browser/mockDebug.ts @@ -168,6 +168,8 @@ export class MockDebugService implements IDebugService { } export class MockSession implements IDebugSession { + readonly autoExpandLazyVariables = false; + getMemory(memoryReference: string): IMemoryRegion { throw new Error('Method not implemented.'); } From 9c15f4185a2d9c7cdd5b83f3790b6fff56df9fef Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Fri, 15 Apr 2022 15:47:20 -0700 Subject: [PATCH 115/245] add favicon and nonce enforcement in microsoft auth --- .../microsoft-authentication/media/favicon.ico | Bin 0 -> 34494 bytes .../microsoft-authentication/src/authServer.ts | 8 +++++++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 extensions/microsoft-authentication/media/favicon.ico diff --git a/extensions/microsoft-authentication/media/favicon.ico b/extensions/microsoft-authentication/media/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..7d1a59f7bdac3916c4461727ee106d2f2e532663 GIT binary patch literal 34494 zcmdsA-IG+ubw3hTRIwAYl~huGNW@4MNRM0j2iQub^2}3S*>aW33i@Cb^o^t$AtBHQ z?DAsAWl0N`ZOI@QVEGatW-XO1@v%GNOFTHqT~+dAS7}%vReDfy^ZT8-=T1*g-;Wts z7Uvd+)7`gk_c`4?-KS5V(<^d^+$BRp0$h=Yz9sT6BJ#cOwd0%rT;v4ODwTHpedPP+ z?}`i$C-LPXU)>`zGSZGe_>Uq#{wI+KQ3kT2#CAH62+A4sIrW?jH`m`4U4Cm>qxG}O zNI&JAc|k^JUXa$QCndi8PNfZ#l}2l_xSW})jLlXho_$f`(@*33Nqp}B>8Ot>t34z& zYf_u7;oAgGKO^z{j!GBQACVd$-u;M7xCJy{l3H_v#3{Ula@I>@{@r`y`JV%b0ec^l zXz!yED?BF6`a@D_zAO`;zap^Usb^S`<;c7f?EV+wSf2EFGtci{XcX*M_M5U|hX%6s?4F7O5OoO@j&Kx{DmobuFQ z+3Cj!T>16)V;7p^Qn|22qVsP`OgQ_xw2q;lr^X~c_=Lm<)=6t>os3u+S9aoi2QF@v z=)zWs2@JEm!|P=7Fv{?t#xkyaaDVK=rL9uEI03rfkPTJ`@rO@I^$5mG3QvKSF=<@g zeSho%^RiB2|2g)IR6oR+F*x|R#8>OfyTJd(j~{J9ba(b zK*_bSHZaUIzGEKd)o<`E=NU{@nuYV1KA5v^_H*)Csa@Le?P&h(dnWsxe~M^1yA}N7)t3nN6EFiDyv9Dg1`!w`2YcfR$fgqeH=>J(y(!B0%f{>)`x8zXS7Q z064E=o^I~J9O=*1R*9Cq0cE@MwuLc&&pa!U&fT29J;3}OaUC$&`-n7HXIl0~8UFlb zX#wIOY>-LTZ|j1Ob)DcGPvCk1Tz^zryB?Ae&hJ<&TJCz`hR?pzUOPz3{4X&72Y_oJ z){!P4Ca{j(|B87BWBTmIuJy!SU!o@XG9I=3U!wc~@Ez;Gyk6&8wFhONz<4;jDGcW} zN!?$2SjH8sUm@T-d$0~pp1@de*D^PJesiZi>>I;Z-oyG50DDh-{&kr+_ZrIgU>5Ck z80!|sK+VnXhOfMj@&mv;SVx9XcK!StSPMNkiE(%U{W1kO@NW_yT8Hr!kXzif)eY-9 z`=&H)opteV)=Rt}eWK-JjyYuTB*yeuXYD18S9W6k3;@>O209i^?yQ ze^hpX43c6Emy%;b^?bBmCL!Czkdxvgl#f#R#+7qA@=B5Z#NR-6x(t{TiKa!C)6omfDhO znOoWhhM6WZt$CPt5>bAuWw0!kiLxn&RRAM^R1O>cx!1_UhUHI7?@JC1(RQXL zqpA<6Lhg0o4C}{Q6p~Hdv{0F4(`Gz7RvN>z%2GT>7K#%4#Y0J+HP-_=RTpmgPUe~p|7ffj+mhP zi8ZJja0Y$r>A!m7*jsR~67W^%Fzht6JSt*!!`)QMQ8ZYb4~Zy3!{0sIl(vxW{wA*$1{j*H~Bm$$n4i zFkP+DfVk;;eN5di+JD5#V*8?=M$cXlbognYZNmD))eVQjUu>1N7ssJ%?uKU8z7BiE z$s^d476ICdd+my=TMmWEANHrKu}^K~?NzNEhU0bALO?KG&_EVd~P|{jM8k{nW2mJz3ZdTv<@&-vuXoz(04%z!C#=s%!2YUAJ zH*X*B)7gN$_YwAAA3^p$hW*!Jt3TP}FT_4Si-uHxu|WC-zg{yhj^a-23hW_9(a!=oZ|L%q&bSbnC%VZ8> z&t;ta5Ey0}-!TvK@*953GFXStIA@-sb^&L z_nVeBzxwIlM>q?rfu5n#H2nnhFCy?vi#iFO*~~ostr+L_)Qz-C^YJs#zes?yvI*)l z&Vc^Yp#LO(*QVDotoj}3nKIjW5oqV01iF|y^)b|kkO!q7^RlNCN_8`T{OPiHdCu6= z*0*^4llIx?rK$QP>T>|psZgiz8QVWguVm@eFFAb7gZ^6upuK|g$+j*mq1~ll$4|aq z%JfsFCmVtuOKt^_cIa**p0B3*v?Q#0MYR7Z>NU&LAEVtTq4Sc32Bp0!kx#e9*#{gy zsuuQ2L-k?kANElYFkXhBt8NB$$gVy)BMg2c$Z;b8j)fi?^U^Z) z+@|kNFwOzS0OA8^uYm3-lXmK#Lv(l28!Q9Bvx`E#xUVaRKDupJX*zO`Zq=Q4($qG< z#**sKH-h$nzPwBt?QHKVi$KpJXm_+mqMbj4jV*Qmq#e5G0YGg< z@q6L9Nf)>}_)_6t-N)bM2+jvuDG$)E*m(gl`+&dgJM~Li^=H2%W{`ro9i^Jpd0wpuK5hMrn8KtpjH^ z!80AS^>|Tx`|m?GZMkV4?Zyjvz;Ctw3D$N0oTo6pSz0q2}b|{otIvUX7{`;L_6kooOLzx&bvbIK86el+H|hkhdB%9 zmJcb12<#h7-SJXeN2tVn{EG!GWfUHx6+q{XP0VkYINJ#p@$FR{0ty@5V9G~O#|&b z&jYHQ{rk~Z{Xxp9@YaQFU=j_v?KY8D%!xju6At*W*_1~Y{BBOcd zq=9$pyTUOI`6BAF#dXe3?HrY7s1_#HIDBQ`BQgRIA5!|<^Hn=n4dC-z(e7K@_O?YB zwzxZZ{t2LstH1`j0(v6SG$7RQowyJ>i_J^BNPA(2em~530##t~fi|+ROK$pS#bx8L zH*T;DfDl49$7ve-#O#ujGw(}VamG8|bI$CZJ4YAB@!LjwX93325YC?)MQ6`h@4;(? zHDZM8MCdF!H*L>yIX^BV7m{;oo>d!w-hJ+Pv2l}y^$Z>BK+x_wFCDTWeXg*tc)aU8 zn?78rjW+C^L-G1@!5J{yb{KR=Mmuf80X&263vj+W&NJR1;OsM$&VC{NTt~X-b!pEE zC-jpCwCfP~811LcLw$jB`YwMq{|w)cv%d~ut)3$Np5I$xd@tI60U*-`0DX|!127CA z{q(t;g3MG%ug~8C&~EKP8-kQp=$Es?98;wK0O=3d^yjASL;Eiv9D7ywz9W_q(v5HVm*G_vyAc`-MK2>bKGH*-U&Ny8Jl(v=)3UA;>H| z;OxJG@f5*VBkqAN`ZH`oLbf9H74=&|yVYSlm@SE6+H`zKISD$0Y`YS(FKOdz`_->w z$jZJ|7(5Jt?k4?A3^ueU=o|Z>gwl zJ#(5xi8kN-4t%2ivhjyq_cqNhs%)FacwVMm5*|9716`Muiod4W5#pEW6a_p-=76UY zTuU$!(yu2NhDmt-JBE=Gp5M@c3Qw&%r&v~E@H4cmZJ#86Ca`tVN6n#XaMNOZ8ZKMJ zbI3^ySV@dhwZw1ajxKEJme0PMPRgkz<)R3!C#~1$Na!*;jc(&3rb8K?4xeZ&jmNbF zPbYXT!Paipj@GW$&f4y@(;5NP0I8i86TGLiJ=86>XUU@L(`TgeU$1?4@_#1&W)?P4 zCt$PEpIz5&!bcW=dFi~vWoG@IU(qIvcM<%@rlGc7*jvlK`v2Yzu;W+-d#10{CJu5n z?|L}>#GSrfS^w>@h&Fziwr?IyI6of4IsSFpbE!=o`A7eM2D!xS={#J%9TvgPuE$m` zhlbP7OAR&v*Sl=;u%@Q`zXO{j0`L7uF~hW%tX!XeyYdeGSu@-EHl7b?1FC$}1|k8q z=}Y+kl>Kk~=i}>Wi#@J=U)l%nV2^Vf0&PfLTSg6|4U~WEE9fh#<3FEozkRO5PVEHd z*b$p&|M1o_xt###$-& zVR|Qy*&(Jlu?PUFcw8gYG;J8r!DFgIrSK8B7 z+bdBS+7>#G@=iO{YTFMv6XRS=`NwZSWgzem+HMSXEUcYskqvBO#~P_EsPSJ*Y(p(h z%rs!-A9BE{L6(myua@#=Jm+EZPn%xOfmj1myVxC${VT_V8@I4K zPApCF;_#0#KMF|gXxRpS+)v|s&I4QrI{d?~uwUC+H@{qu+>-F;| zcFM3t6}{8Ku~ohid*zUQb7>o9wuNg4Y+i?Alp#xkjs0U|D5|` zGHlyBw(z+B_))2=Ek4e82f!`dQ8@+>1Kvjo`de!Iek1(Tem)s1uq_`0NC-N-lxT;* z-8$?;*aSzZjXc_+Oqk<=YX#(lc;`bhF*%C9*(jr_jl9{)lMg4%_ZBxC`X)*18Sg#) z@7T=uYh$nZ+cx%{yGO}?V&kptz_0^4MiSoD2K*uL|3ke?^vfJRI=%y5Tfj~_`0m_B z+}rWE{1rg*U?85|HUs~#__8TGp==a?T?;k-9Q$9-i8x-X|5C0W_ zlz}`Sgfj2U&%!X;U>uP8SyqtrY5hW7yjWDv;p0rBq259ZDAv!K5P@6FY5@9+*=#&`xY zN?Bi#f_%}ZugE_DWKih%knryU*xkulVS7)Uq4 z*YA$yLBh(j>$kyh#@%TdJ|I1QLgd?pI{fO8~hU5ah`p4|#EqSD$y#zY2ewqLuo} zyqRkqgzWr)?eQmn>+U1{j3uf`tPkIBoTCN$C znbeO6V*u+5ZL1^xZ4rPB3OCHZ<0IwzOTqUg_kO`HKdLt{CIT=Ed62euI(MSa>H9@_ z%JHF6{}|qh2#`S_^+B?Jb^bO>`i^OQZ|^MJ#$A45Y=fJ^JN>UHD_WnEZ}d6uV$?ek zVZY(}Vj;b+I|WSNkn&`_^@|ffE&6H^c(-F2AFaGR@PUf=V7#jj7uq1mH}(wld$PMB z72^&<_;3y9`EmL0+CE&?xA0@>;@{%L243ETZ}m~&-IDO(8Wi}qhmJ!h%R6Z4ySAUd zbEm&c`hl@s#sE?VQopKz4^_!`ZU2m|(hT^7wf~+1+hj{2MD$Z&pDW+VShq2r@{aaN z!^s%XxR;LaO6iF~Ro$M<6ZF~A*? z!{|HSt2(Ifo&Q~nf1d*&A;|N=EAuYMUx;Tf4gPxSFB$SvQolQ#O_cT3(|i~cnfWHBMT`)O7# zZiBDsHjDxGKk%UbrXl}S@HFoGXcorVX4pR|u-10J>gy-uI$n5}S+kpL#zo+2v--lnc=PQkVkFH?ezg>X- zyYK;Tn*VeNv;TDtr2b6%e{lZM?_*#7@*cUJfNcQ(>w1WH{T&$plmVbqEb`0FdXGLI zXTKNu;V#yD{kw{=^HpDT*!mgPIe>EDfc(=>9sPZu_4#7)SEgR`p(gL<)1LBA8G!xo z__C*c%{2Qv7RmeD!9TwY&wd_j1NG6s60up0cg*#F8u*1+RW-)VOWD4yTH{L8+_x#w$|=_h#` z%x!#IJD{Cd72C_&$=VIIX}W%h_h~!NCF!6| b`vD(nzu?1mM<)H2_TzNYuX9O1qu>7zZLY>L literal 0 HcmV?d00001 diff --git a/extensions/microsoft-authentication/src/authServer.ts b/extensions/microsoft-authentication/src/authServer.ts index c36e56175de..de08c6fca0f 100644 --- a/extensions/microsoft-authentication/src/authServer.ts +++ b/extensions/microsoft-authentication/src/authServer.ts @@ -109,7 +109,8 @@ export class LoopbackAuthServer implements ILoopbackServer { case '/callback': { const code = reqUrl.searchParams.get('code') ?? undefined; const state = reqUrl.searchParams.get('state') ?? undefined; - if (!code || !state) { + const nonce = (reqUrl.searchParams.get('nonce') ?? '').replace(/ /g, '+'); + if (!code || !state || !nonce) { res.writeHead(400); res.end(); return; @@ -119,6 +120,11 @@ export class LoopbackAuthServer implements ILoopbackServer { res.end(); throw new Error('State does not match.'); } + if (this.nonce !== nonce) { + res.writeHead(302, { location: `/?error=${encodeURIComponent('Nonce does not match.')}` }); + res.end(); + throw new Error('Nonce does not match.'); + } deferred.resolve({ code, state }); res.writeHead(302, { location: '/' }); res.end(); From ed6d3601823a41814b9c18f29d771a808b36373b Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Fri, 15 Apr 2022 15:48:29 -0700 Subject: [PATCH 116/245] add fallback auth server for github auth and better fallback logic --- .../extension-browser.webpack.config.js | 3 +- .../github-authentication/media/auth.css | 100 +++++ .../github-authentication/media/favicon.ico | Bin 0 -> 34494 bytes .../github-authentication/media/icon.png | Bin 0 -> 3818 bytes .../github-authentication/media/index.html | 37 ++ .../github-authentication/src/authServer.ts | 198 ++++++++++ .../src/env/browser/authServer.ts | 12 + .../github-authentication/src/github.ts | 6 +- .../github-authentication/src/githubServer.ts | 374 +++++++++++------- 9 files changed, 589 insertions(+), 141 deletions(-) create mode 100644 extensions/github-authentication/media/auth.css create mode 100644 extensions/github-authentication/media/favicon.ico create mode 100644 extensions/github-authentication/media/icon.png create mode 100644 extensions/github-authentication/media/index.html create mode 100644 extensions/github-authentication/src/authServer.ts create mode 100644 extensions/github-authentication/src/env/browser/authServer.ts diff --git a/extensions/github-authentication/extension-browser.webpack.config.js b/extensions/github-authentication/extension-browser.webpack.config.js index 0722e3e572c..4fa2d1aa902 100644 --- a/extensions/github-authentication/extension-browser.webpack.config.js +++ b/extensions/github-authentication/extension-browser.webpack.config.js @@ -22,7 +22,8 @@ module.exports = withBrowserDefaults({ resolve: { alias: { 'node-fetch': path.resolve(__dirname, 'node_modules/node-fetch/browser.js'), - 'uuid': path.resolve(__dirname, 'node_modules/uuid/dist/esm-browser/index.js') + 'uuid': path.resolve(__dirname, 'node_modules/uuid/dist/esm-browser/index.js'), + './authServer': path.resolve(__dirname, 'src/env/browser/authServer'), } } }); diff --git a/extensions/github-authentication/media/auth.css b/extensions/github-authentication/media/auth.css new file mode 100644 index 00000000000..45c42c75ad5 --- /dev/null +++ b/extensions/github-authentication/media/auth.css @@ -0,0 +1,100 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +html { + height: 100%; +} + +body { + box-sizing: border-box; + min-height: 100%; + margin: 0; + padding: 15px 30px; + display: flex; + flex-direction: column; + color: white; + font-family: "Segoe UI","Helvetica Neue","Helvetica",Arial,sans-serif; + background-color: #2C2C32; +} + +.branding { + background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJYAAACWCAYAAAA8AXHiAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAhGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEAAABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAEgAAAABAAAASAAAAAEAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAlqADAAQAAAABAAAAlgAAAADkcSUjAAAACXBIWXMAAAsTAAALEwEAmpwYAAABWWlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNS40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx0aWZmOk9yaWVudGF0aW9uPjE8L3RpZmY6T3JpZW50YXRpb24+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgpMwidZAAAxaElEQVR4Ae19CbgdRbXu6j2cecoECTIkICCGzAg+7qeQ9544QFQgiXpVEJTEe59ALsbMwE5AMZCQELgKeSoqDlyiQogCSUAC6FNCQhIwQMALCbNMGc68p37/v7prnz47++yzzzl76OCu851d3dXV1VXVf69atWqtVZaUg397IPJwSCKT46zgx16wh+19XSYnO6PjE7H4MDuZ7BSxXhQJ/On5KTWbTSMm3bYlvHXmJNxj2SatFLFVioeWn5lDDwAgMvOU2NG/3DMo2h691rLtr4bqBtVX1NVJRUOVBIIiyYSI3RkXO9q5NZmU5S98tu7XpmQF2AwAzCoNwMrAMm/CT3HkrgqJTI/KwodOD4ZDa5MVdUPtfe+KxONKiSob6+SIE46XqsYGK5FIBoLVtQEJiSRaWp+xktaNuzpqfirTLcBOZNJtdnjrDCk6wMrA8hOgWJcZoFSrT4nJogfPESu4Dv8AVLTTEqsC9MkSDHB2NIYfkZGnjJOqpiZQrkTCAmWyKqtDgXBAEs0tLyZtWRl7953/u/uiUR0sVgH2xuKERCJJnhc6lIFV6B7OuXyAJrIpqDzVlQ99TUKVt0scAEomQaXskBAONlEFrgpvLdkZk6r6Wjn6IxMx2qVeY0IsSVrhqnCgMiiJA62v4a5Vkmz/4a7PDWtmVXSIfGMSAGYVFGCpGvGh5VCiHohEAiJXi77sKx+eLZU1N0hHCyuTAJCCDqCAKIUCYxv4AcY6YnL0qeOkZuhQSeoo6b5OG+AC4ZJQRThQFQbAWt7Bvf8ZSNg3P3deA8ZUF2AzADCrMAArA4u9XMoQsQMp6nHlH5dKdcMcad+fJGVCCKRAxfMU1XKB1dYpI8acKI0jj5FkDNSti3LpzSgjCQQmrFA4HKiuIMAO2EnrtkBb+8pdXxr2OjOd+bAd2vQ2cro8mXPjwH/LwBp4H/a/hLvuCsr06cpky6KHfiK1jRdJ235M80ClABMtmMMfKJQDNB4j1VCstg4ZcTKANWpkZmCxAJZiABYIhgO1VRwi21HGj63OxI27pje9xGz5BlgZWOzVUgSPjEqufOheqWmcIq37YwBCWKsDDGlQULnAUh6L4CLFAh/fCmCNOSE7sNxi3IilxiUQCAdrayR+oCWBcn4q0eSy56c2PMc80+6yg2t4MEAKVgYWO7HYwcz8Ig9XSTz5kNQ0nC7tB6KoBmZ+bnAZdaVUhIMHVDxWQtQCYI3thWKZ8rrHLICiCwCslmIKFGn/GoPm9S+cW7ddswJgZw4Ta9NkC/n6HsrA6nufDewOV/Ap8x8cggHvMUztTpLO5igkCV2g4hMIJI3dY56mqBdwgXOlWP0DlhbNkhVgtoQDELza7Z1iJ2J3J2P2DX8/r+Evmsm2A2dukkBfAYbZSDkUrQco+IQ0XeY9eKwEraekovYkzP4OBhUrlGLEXYB1q6QnzXPYLUtuJ5SLcei1k62tMYDKDtTWnRusrf5/J9zbcv9xa5snc9aooLJti3xYbsVy1lEOxekBI01f8NBECVnbJVx1hHS2YSrnGf4y1SQTwAyYTJzpvr6lAWA2AWYlW1vidrQjGait/VS4pvaPx9/bvOn43+4/GwCzDcAoC0P+rKNd1ot9q1s5d489YIa/BQ9+Ap/yAxKsgJSJ8gGXUc94I1BD4Og/j91zDodYGFTmvf88VsYnpiWSt7ICNXWcoQoo2uNJ217298/X/8bky7ZcVKZYppcKE0OaDg0FDn9XPfwFCYc3YEYGUEUpTQeFUNTgyZniDBUyfJdecu9hVJjAYS+YbGuJ8z9QXXNaqL5uzQlrm3d88O79X+Ujt860MIu17ExDZJliFealkMJYMn1NQNZATnXlQ9+ScPXNEm3H02zKrZQK9PpoxQ5+PBJ3pVwqbkBJpFhjPiSNx47sWY7V60Nyy4BaQDQhdqCyJgRWX5LNLbuA8xUfaKz7sTtEkkhhMuBoU5QpVm792rdclKbzkyWoFj64WCrrAKo2SNMxhukSjb4CvoZe/pGBebyfP88ZTNx14KQX6BdV4McQSna2JZItLXGrsurEUEPdra8daPnv43934CIy+Qoqth2hDKx8v4hpkKbrAi++3AUbf4AlmqukvTmuiytgWfr3OBdg/bs533cpwMDgJ6BFEQtUVB4dGlz/kxPuab5TH8S2A1z9bGi+6/o+KY/8FKkUw6KH/ktqm/4Ngk8y6UHltvVCH39IrQx1SsXmoI9l5Tc72xS2Y50AWGtnaEjdF46/p3mtPuJqqmOUQ356YMZtYai8xITrfzsGb4Dg8392W6IZ8FOygImXslwe8KOzF0Dti2D8vZbO0KC6z37wdwfm/d2yvu8dvbPfXr7acw8YcULkrw0Sa34EoBovHZCmq3Jez7dlvwKkGMCoqIHn+E8tQtse5n1UwZn37HXVq0nOeO14sj0Ws44tD4U59FjWLEaaPue+IyXWskMqa11QUfCZQgaK6OtxhqeaIkpInjLUyiRBjJKMhRrrqkOh5IXlodB0S3/ilDR9/cngyx+RcOVg6WjtXZqe67OUUqVnJroQsgu+nTzF/oXUlsIUTIk/WaZY/e18A6r5D34MXbkV0vTBEoVKZ1Zpen8flv2+FCHLnq0YV4N2Zwcwb48pU6z+dDfVXiKnRKGh8HksJt+tIp44penZlmj68yBzD6DjRQ8pmQbwWR4u2aSau0oQg2JRT9EaUqZYfep9vEaKFGhFs2DDN6Si8m594Yk4RQyF+0gVMRlgwyTzb9qRIZu51C02+Uzc7WLaSS55eIvJh1WHwnVGWt0O+VM1eFgMg4dIXBY8OF+qar8HlRd0JciHRRutEoXUy/Q836R5kjIemnwmzpjJTcwlD7O6+crAytaZ5ppK013B5/wNy6W6/goIPkmlMBD1V5puCu8h5gs66GV6E7zHPZRRsmSrgOS7ZI3K84O9Bg/zN/wcuulfVYMHZ+3Mw+Hk+bmmuHT8pPgrZki/aG4qfVymWNneAfmp6Y5TDpm34T6s+306v9L0bA/ntSzAMZdK45qht4qXKVaPPeTM/GISWVcjnZV/xPB3mho8WL1ofPZY4AAuGHmWAZMpKv3cpPsgLlOsTC/BLNHMXn+YRAOPglE/ERoK3a1oMt1XrDQdDsne4YEEl/kv1vOzPcetUxlY6Z3kLNFEZdGG42Gk/hh00w9Xg4fedNPTy8n7OdBjgKRl+5RcudUqy7G8ADDS9DkPnApQbYNjjsOlsz1/SzTeZ/V2bKiQiZk/HUvp572VWcTrfaNYVLddvTUkgyYl5Rk2c1NARtRbUkIHX3nrKyNNn/fgp6BCcL8E0DUxuHShNL3ULzBFqVCR1KwwlZi3LshnQbkDizMkS61i2dkmqDa2zMQpr7tuDc3FQyOmNH0TtD4hTZ97/1ckFLxDEniBCbpvUZMofzQjHdzp5/6oZaoWuQHLMLOrXmgQO34hvugzUMJgfMnvIH5I9rXfgRfTJpqv9P4vU63r7cAYPESmx2X+A7NgQLoCtn68q7BLNL3VK3U9DT1pp6lsPjzoBVj8mqFWSzOfW549B7ZwP4W67RB1fgm7ACh24T84DZTsKlm2/WKZOX499Z1l512OdYoPG5yqkho8qBAoIXMf+K5UNiyAch4axbdXwiWaVAU9BwZQqdHPJCAPD82/55aSHbp17BlY/JrXQLVmOoa/VTtnSqjqVuFI2LI3itkJmX6nCLrICVceIRVVD8iNTy2RK6yrtVGGypWshVke7Bg8kCqJzF2/Gk45LoGMynEf5HGPl6WEIl/yAIlPTjstcmWyP86tW+ZZoWvCo65sbvrblVLVcCuGCFtiUb6MCjQshH/oOquAtQJMbgKzp6TUD7lKVjy9Ua57bJAaaUZ2dnd0kb1KxblKXtAYPMxd/zss0VwibTB4sEGlaF7sl8AXZP61TjhxX1qqirYO2alTPx0c3JHetbGbdq6SmqZLpXUfAEUnAo7NWA8NYLNjkFBXQJj4NpxkTpMrJj6ixgXPTLNTXut6uLkoyTR4WD0zpg5kB7+3EbrpZ6g0veQyqh5abyTuRBSP1dsM8iYwnMBTn91ph4afdKw0jDrWtqForq+oh6KKmWyDoegOLO/MbuXOO6V20BekdS9ngRwyu+ftsaZ2DPIfmI8je7Rtvlwx9vuatdRDo/FJNe/3gzDZg8FD3RjH4KE/SzTsinTy0WOH9OFCWrl8hD4GPwZYSSj/wmzBqm2y7Ff23H7cZ856Lzxk6LfjLc30nFw69R1PKwmsrqGQL57iAg6DK3c+CCadoOIyhnoh8dzXy6EVhvvohMQ7bQyN18mNf1sHUURdSYdGCj6pnDd349H4RmDwUD0AULH5hQBVT+WaZwF0YEYkGA5KKGRZrfsWyS++fHGgtvofYEpArArjpLaXl93jZYd5N9SE4oTkM5vAd0yQln1k0vvLI/HLsZXa1TbCX7n1nNyw43z5zujHBZ7iVLhaYHfQqRYbafrcB8ZhDHkEk5BG6YD7oP63LVV04Q8AKhIxGuZjsAOVBZtx4C1JJKYmb/78Y/r8aKLKrsYR1A0NBAtfryxPcIkuPPbajjeUVTuOhBcUfM11E6Aa4iy4sqb9/4fPJVCvNrhADFd+QCqr/irLd8zSCYFjht3zjDRLvft0SakwdniYd/+ZuG+LhMKNKk03fj77VFiJMiuDjnGwuoG860a4ED1RAKojbttSwxrB7zYGHuc1MS55AF4IGUjLIUO46RksuNp/lYpqmC/Bw1x+mVln1mhB07Ju6AoMjf8iL738FTy3UzhrjIzm8/IfyKjTfdCc+6bC1eYa/Z7jMcfggS33BsPamDjTtfQ0c55eFtO95XiPzT0m5jUGU0Z6XhvyQ3g6ViXVtr2LZeWUiOZH2044YVL0dT3x5w8oFhys2rIeqiEwX2rPN6hMqx0n+G37YuDdpsqoY56V67eNV1DphCHSxeuZO/obU/7GMjn7m/PANyFNX6MC3XiCzvgpJjk4mDQTe3P0lMb0TNd4rzfde+wt1+TzXk8d66gWxUYC8P4f2yvtLWfJjQAV9e7JA7NtPg8haTzsPzD7GyUt72GbMqksYH35PWJoxDBbUTUK85ptsvypb8q3x96mz/TOSPtbCTV4wGvlJGTu+kWwSr7GFwYPfWoP51RoA4e+1n2PSqdMlZs/97aKSCKngOJGUvDrXiyTDQnsfqX4Z7bOCqc6DsHwNRcnYGjsgJQbHVE3+FZZ/vQdcC7u7CGDnar6XQUtI+L4aJpz/01gdK8BTwL5Gx9Egwd2vJ//teUY+kIBsCRBgGqpLPvMGXLzZxxQcVbrNCCti5wFBCfRL+2juMGW0QoseAzRd8D30O2fzUlP6+nc5DWxN583jUMSCm2Dw/y6QV+R007aKUu3nqRrkpxMcDjrS6BQ10jT59z/K6luvAyg4syPQ6wLqr4UWIq8Nih5NYe+Vuk48FmAap72Az8YB1Q9VwrdfNA3w9wm3RybOD2d5yakX2N6+nWTZtLNPeYc17lXC10yp93NcxM8uU1Sj7HJa2JvRm+aHjueelsh1qiqOREzx7/Jsh0X6mSC7gY5NOYSyKRz2xC24zv3rccQ8iXsRYOv2/ECnEsRpc2jQ19ch77Ols0ST5woy6as06GPn5f5YLJV0tu1Jp83zRybuKc83uvm2Bt7jzOVwTTmwT9dGu6ERS+TvDSV58UKFViH5EaNAfB6PwXftVofTD6J4oJsQaXpYGRnr6+V79y/GUs0ZykPR9/lbgN9G7NdNjeoDGLoqw2h3jfJDWefJiumvJbaszBX0VTf6Hu2Hs3bNQwT1m+kgiIRiB1KFSxYZFNLghoGdYMvkRuf3iHXbzlOxQUEV6ahcZorTZ/1wAjoiO3AyzkFwx+c8XORHKjiv5+DLRz6sA8h9nZuOzBdbvjMLK1uLkOfn9vl1g3A2r8CM8I9mEFV4qU4MiXvOzHH3th7zIK85+nH5rynfCYdW2ugnJBK/Ctrx0qw6hm5fvsXFFwcGimxN4HS9DUQfH573YfwPTwFg4fjpJM7PKjmhcnlV3Bx1ucOfe07xI6dJDees0Y4pNPFRy5DX1cLfXsUkCtOxxZjyU/Br9M+fEFUiekClwFFeszmpKeZc+8102xeM9dNbPJ5z538FbrkEghWQOZ1pyx7apUmczcqUi/VTQeoZq37H3g9T8J90FDpgMGDoVR8kLdMf1EuZ8ivqgthdWO1LPv0eFn22ZecoY+yKX+syjivYWC/kLxjFjYba3k37RwHbYQ/gU85qgDS977WMgyhZhL1SGJovFSWPf1ReIs7T2aOe1ULunTt2Vgp+L0EQcSiEF1YWDqiSoluDwKGg++HE0vDexhwmfO+1iYv+S0ubVHUksTQ9zVZfvYdWqzK71SU0L+nOFIv517vB9W/0gZ+l9vHkOSCtyIluHz0yxK1x0GV5GmdoeCVDfwpAyqBogJ82Zg1Vtd/RIKBF8B7nSUX/OJf4drz90qVYjHOBimecKgUY90ShOfuMaJU8B6nEgt+gFePpZlqLCBH25/DBzNaQUVAkXfMhwEK21Wath3ceW5d+PIkxSTPH7tXmk+eiOn6I1Dwo2ZDqcHF2nHxNY79jaskWLleRh33S2irQqVQfVIFHSDh3XHWrpQJLdPGeWMee/9ZbFECJkSY7VbVYcWh+eey7AmAaspz+iETUO4uDgOuSUkpcebad8mKuGCrZFlnh2fKsr/9FtP/86TNVfSDWkbmIoqQylljPEaib0lTExaGwra8/lpQosB9GE1QIOEqPxPW0tSUMa9pwEnqnIkmk3s5/1EUCo8V0E0TiBJmyvJzVusj2Mfs6/d5cCiWaSS/IkqxGWaffD70qWBoMIgyIcq4Uq9Irxfzx3myA5t43JKamoCMHClSXw+aSlkoMpihzwyFhKFJ1/vdPE5ZXdfy3Q5oTqFI6E5BRTvW8SK2lx+voNKPNgLWA338TxC6A4sNphRbjSkw/s8+eaa07fsurFhI2dhhfF2lDWTQoaggQVTpA0eJHHY4XiPOwetrDVNgwrnqiBtApRDl5DOtYP78BQ5vAlCFsSxzl7zcClHClB2iyoYAVARrmf8koWso9DZYFfHwdX0Y9oHTRy/CrOxt8AkrIZJALjpcpm51Xl+I9+m9H3MU05UQxEOHiVRXibwO7SRSrwoOjahbgJkQWE0e8oVztmji9Fkj0wcWMPRVVKiKTtuBy+XGsx0xiVKqyYXlVdlG/vshuN2YGVisoH5dnLWoOOImyJPeAc/wC3QcGGa4xnUMLErfFFaltk5k5CiRN94QaW4GuCBrVPGDCyRvLfkC2Hj+6zEO9Nh9MzzOOWhm3hhXteGOlldBTc/HssxmxzrpGbsYQ182omua21OT3FZrFzBPT+e85u0ak8+km3OVxOGkZ2DxDn7iEXQaxREzx/4SDD1M6m3sEAqO2ewQakrU/O6Pvije7p5785hrvGSu89jk8V7PlMdcN/eS0pih8UgMje++LfI2qoklOLXUZkt1wOcDkFfv1x8cI874Vkw+VgCB2RlMHXmsRejegwGIZ8JYjlon1rtfkhUXtOrQNx1C3CIFVstbtfTHZrtm8qbnST9nvkxp6enMw/+DeSzmTA86Y4Qa8eyT16NHT8UMrRVGCWTqM89uTA3MU7zlmWtMM9fT09Lzm3zmHhOn0vGWafJPkAw9TOQoAIygUb8eyJxi6HE9xXchnflT/ywUQctkunPaleY552HStZgJhi3M+uZBNgVVF4CKSzOR4oGKVTG457FfQm7AYm2pm04d9dknPwFqMB4znjexvkjtg6J9mVk7jUBiIPWqw2zxmFGC2SNqh+oRPBnB5QJIQcRjPXDKMYBzzry/zARVH7Q9EXsL1uEfx1rfUkdZ8dBQG/Y2plDHuQOLNTDgumL03/Gmxkln6y61fPYLuFhHMzSGgfkjj3aYezL1pGgMBjA8V+rlUjoDKr3uZE3l7zoFakHLqDbc0bIRVk0nyopzHtO1Pi4eF8ukras+vj3qG7DYDIKLPNd3xr0lweYJ4C0ehx2iX6T0TkcTXAZIFEccBYBxFqgyL2QheMyQmKJkSNc0FpGJenHoCwWx3hfAWl8EVOosWXnuPh36etPwZJH/ZKEX5r2H3lCeC7PFKyzunv1Ruf6pP0BK/xnIvEAaqFvFt1jqgCpw0OLQWN8gMhLKjG+8JtIK/1ecNbKGJGLpNeU9BCbTnWOagsZgMQOFRGiAWPHpECVsVIuZnR+Gh8PpmflM3F6UwDawnvz3S0Bd+k6xTOW5eG10pOaMPRuLxXCuTyl90hkuTL6Sx0AIlxXDIKpHjRQZMsShXPTa56VcqSHQTdcXBbJnQ2BWSWPR1kelDUPfcoBKVXci9vtFd6oQr6j/wGJtqCNFjUeGOWMuxBLQcixekwriO/KRLwHv0Hj4CPBeRzqgov0qAaT8Fqps+C4m2kkYY0BmEa4KghIvlZXnnCG3nveWZ+hT6LHp5XBwD/RvKPSWo0wrZkMMc6zZGBbfgT+t66DRyY6nkagDPM1Qwh+Ci4FDYwMWsisprcfQ2IGhkYw+KRazONloLIq1vvZWiFa+BJP2dchgybQ1MBYt8dCnjfD/z8AolmkfZ0NXA0JcvpgDt0Wdzd/Al45XFODSD4dG/wQCjOCiAcnRI0WaBrtMPb8D12KmAgvIHa2bpS3+IQUVhz6G94nasLalwD8Dp1imgo5ukSulH/NjuQGUywrdA78JQch7yGLmB8TmeQONCS6CbPgRWDSGu5Z/vAF+CkNfTS1mfXtXya1TL9dHqCXQIaLmwm/DBFJe7znT09PMuYl7upfXGdLLc1K7/7p58/+yOWNUccTYtVhS+QgsaPaBcPE5BJd/gndoHDTYlpHHBeB36oC89drZCirObA8VixkltmldmwkE6Wnm3MSmiEzn6Wkmb3rMfPjPP7D4oK3u0/78Z5FX9jhMsVq551o79/5iRDo0gomvAs818liR8ad1PXVa12H5qA89AKqVf2BxrYwCw5lrPieHD31C2jub5OUXk2CCAw7L5UNwcUxMQKHLCjTAKvsPct2Om5Bkq24aqW859LkH8ggsDB1k3uli59/v/jqMYO9Rvj2AmWFHR0BefglMMhza0LKGMzD/hQDUgRyj2fpBl8l12zdL5K9HpuwBVHTvv0r7tUb5AZb6bVoMYEFL8lv3zINu0o/AsBM9CTCMWAbBHCEOATXB1Y7pvU4WcZk5/PXfZTRLy6Cq6ufke09+VsHFehq1bb++TR/Va+CzQjK4EXe/5MvWLoerSeyXDPdBAVCwpOvFlxQqRHBhJrZnt7M4TOU8MzPzUYe4VaG4gS6FatGetfK97ddjaJyLa47RrB+NIQh8PwTOClGXgVEsgsrIdi5f+zOs+l8Bwahj7UuzJz5E//mDgDVcPX9lt8iB/Y7euj+HRdbWMZqNtiakfvAc8F2PSmTHYV1Do7aE+UobONf2C6jYE6gL33b/gUV+KgWqdX+AT6oLoOnARWhnhwcDKM669JhPRSCPxf/XXhHZS01PHPuqZ7SW5of9E3CNZj8mVbJLrtl+loIr4oojTM4Sxn7CFbuB9enfUKhakpNj8h93VUuyGvsl130UVilw323R94MTCCg9YYygp7hI1RQaOpB6vQEDCA6H1PrkOp03v97kgx/HnpL6V1CbqWgCv7hevrttsSy0Ilq7EgtQ2Wump33QW6kq9B1YpiMvvW8YhJ+PqdO0Ds9+yYoj/Ojam56kHqYHmgRwMaZFzVtvOuAaNtzBlR/B5bSAGqPYGAFbi9QPuVq+t+NfJJqcJpEJ+4QuLrlDWjmkeqBvQ6HZ4eGydcdLAO6DKqpPhI4SVZOp6OeARYHhAsekKZjwo8OiJ+Z16kbRAOLNV3nm5NElO5RhyvJLTHea3MiJ/iRq6v+3VFjPy7VPflxBxRmj2mNqK/7pf3IHltnh4fJ7P4IZ3zZoUg6Hdxp+pQ6oTFfqEOieKKBwzDh17AKLwyH/mR/meLJ/L7QN9jhDIvdB9CtTr0qMNneIiKEPhmH7lEfku9vnOoa+WIw3C9amP4oR+/AbzA1Y7Cxanlz++08CCI9DRbcWPgnIqGeWSnvBpR0L8JiYh/qPH+bTf6RREa8FNoFcAqKtoJ/B5bQFewZhO7141IaD3u9DJLFOZu+o1VUHP26np3Uu3k8vwDLSdCzRzFr7ZciiHgAQsPwBuyobfEW2LyVFokxjCCIee8BkQMV02v8RXNSPenk3zOYxwhpBKm/LFkw9TB6eDySY+03MsjIeu3I6qmTXYM+gwfYuWbLlNLULoHZtUYdGVtD8pzc+vfLefOnXvPem5zN5M6V3v9YzsChNp2Ibpemz7p0l4dpfqOeUJFSPnQ0wvTXo4ViR5FxLgYqnBlxpMTuGSnc02SK4OqBST3AZIwfTnvTYeUL3fk3P05dzlsf8DOa+no45a6QzXe4ZFKr6APjOv8o12y4v6p5BWlHvj6m0idMrb/Jma6S5Zu5Nvyc9vft5ZmDpEk3E0em+7N5rIX1eIdFWzGyVq6bgKfdAqmSCF1wpapUJXHgEh8NXXoILoBa/C1JN6+CyqBOUPI7t9AatxKzxLrn0vkp1bPdPODQeDCxdook4QqVZa2/D7GchZDjcL5mwQH7zFfQh5p0KKjc2YPOCy0vF+Koo5+KzXtkNfwz7HHBpAp/r2wALJdSNGyPUNE6DEuGzsuTJcY49JgTKzpYsvq18PivWHVjdpOlrfwvd9RmuM/787Jes4HIB6QUaW6Tgw4+Czb1IqTyZ+NdeFtn3btKR0pMZ85GhxsFvg5V39wyqHgWB6na5ZutMZSnoaIV9XKrQn2+yP/egfV2NNNJ0xjXDN0ql2S8Z0vR8BgLHK0pgxV0cOY/xnDAflU8tGIu+9aYzWRgCt0UJOF9znMH1bVjOZzt6L8vZM4h9zD2Drt3+LxIf97WUz9d8LWSbBYtcAZBrPm/7+noP8jsUS5XzoEc16+4mqR6+RSrqzkjtl8xCc/lnRUw+7zHTTEgde8Bz0LCIzLys/5oPFjPwkxCQDnn+2bPgYO1fwSTjOrh6G4Ya5pn+jDE0grpS5lXb9FUJP7VTIls+5Kw1gnL5wrDXvJz8xpj1oYFUzrt83dGSCMAZf81Y8FQHO+Pv7bkp0CCj95j3mZdujhl7yZSCCyDSYZCXcBzACwnAKLYGxqLxts2QPxwvd39zo8w/+ddQGDxHqR73Rwa3rMX59kc3RjBD44cw690JvusCHRqppVrKobGAfeaIEy5feyImfNg2pOooSNO7lmgK+GAt2lArnihxYkyAYeijAUYl9pjpaF4lt00/TX70lVfVWJSqwgvH/AE5T8eQ2AEzMw7n7jpdOqJxJWPIlC/XtPQCM92XMQ/ccbt7BtU1/QwiidWai+Kc96H6c0C+xh1W7fWQvzTBNVHxQGX6vhu4lGphjxnsLCrJKDZvmp4yw1KLGVBW9RsBd0oLxv4FIJyAOr+NZRXkx7ZsGviie/tnxvQ8uablcl9PecBv2VDj0D2DBl0i127bLpEnjtU26VIQBNLvkxCQ+v1XQJfqGHxNUEjHGthBHZ7eSQU4d7qTbChcLtLbMPaYSdgnyQ/Py7zHjHGntGDscxhaMHS3/re6asy0XYvBi/eFsQnpwdsscy0930DymHsdvha7fWEhu7J2HDZAfxYAm+bsSYih0fjDMHXoT2yeZervPc+WxmeZ6+bYe6+pS7Y0Nw+1PKfiq+dp1wzRFFC8OA5+Co72MfS1H1gtt5w7Xn54/otZ95gx7pTmjH5T4vvGw2nHFvVbZcDFupsO8B6bjjPXTOxtqzfNHDP2BpPONHOcLY+5tysvvdeg3dgzqKr+Llmy/SbNYvYMMvlzibvK7A4M3ptep2xpJq8pL/3ZmdLT09xzzgpH61INfSyYTMWMsYCDWV4Iin5JqDVfAFDN1PbopAJrlNmCDos2BI+TW2Th2FMByg3ujhrZ78tWZlGvweWTs2cQttNrugx81+MQS3xAh0blu3IbGv04ftIqJTOqC93BxtF+pTrafw6mYqNl1bl36CyJ03AytbkE406JM6xF4z8Jge6vAK6wTgDcvWNzKaaEefhxO0NjVd2pON4li7dOUXDx3ZC37CUwmwnmdZrYpDP2ppljE5t86efp96XnY34TzDFjSh//Bv/kvAYd4aIFGFzgj3vMdGCPmSHbR8stU59z/E4BUI4fiNwr43WntHDcl8G/rJJqgIu+rfwtpfe2EbNG1zKouv5eiWxbqv1Au4JeZo0GDObFegvNdC1bPnNvT/eZdJOPsTfNlM09oddgys7ruVEI5hxIsDj0VYKfAwFvb54Jby4XYg3NWeoYiMtFvgCqqZDaLRp/OcB1JZakKOci3YLDD1Ta7/9JWgbBRq6TlkGD5sjiJx+V2X/2n2VQDu8/IC17V2L42A1xA/z64KUXKujQR5eL2F4tFn0RBHI8+KnVOvSpNkWOQ1+2+tGdEgP5s0Xjr8WOW/8GgS9oozqOKCZFzlbL7NccXjfoWAY1fEzqq3fJVds+4YhZImARXF9k3lL89MGwXqhPQH56UQcsts6CuOE93ahR5UF5rimXXshhco+Z9pY1MrTzJLn5vB3gHypSi7PejhrIsUqzAVLKha4afytEF1NVpyug6hLFocoDqT/vdWR7mDU2O5ZBVdUb5KqtEYey4+Pxqj87n9JAn5i/+wkdBGdJZ9WUFyCQxA6rHS/hCyfDxSWd/AwdOuurgHYEFNw7sMfMLZ+frmrOpCrc17lQgcMqeZNF434LmdhkDDHc8zAEgB8iM0b2P3eOhWVQtD0J58FXy5VPbpBZ25oo83r++TfyqxyQ5/fgLOlwEfrm818Vq3U8KNc2LEI74BrYwwBNDH0sK9b5GmZ9p4FKrdJZTr6Gvt7qZ6T0V47ZBHP5SRCr7JMwpfQ+B5f71euXzaGRE522/di0oP4TUpvcJXM3f/z1mUdAhxvB5jDvv+BUiovQpCA3f+WADN1xCuRJD6oEnIx2/4KjdVCFPWY6WtZJdcuJcvO5m3XoUyZbFQn7V3Jf7zJS+oVjnsIEcTx2Z31FKqAtQUGqUmXi32f/rJjWCY1lHeFgCb/caTYGqnsY+OFHAnO3LGJXBMIWWBkeaU498MOPUyVTE4LLyI++dc+vAa4vYvji0EGpfPe85p70mGAM0CoCIR6dJ/953lI99patCUX+4bBICjbvsUFwB/kIpPRj8KL44fhzSCGgGBh3Axn41aQdsGqHWPZbr99+zLgT3gsNGvrtROuBBF5QrzIvLbPQP6jzwWDxOvr41t2rAK5LIWvijIp5s5FdKi9j6KvlUsVbYECnQpTwmA59o6fZUHArPZtJptfwXv8IboT68Bk6xPgRXClg4cALLvZiEjIVKDraiXBo2PB6qR9xmG1z59kMr7PQGMpYPup7MFCcocqRB91y7mUA1VVYLOVyDypuPCBrS1EmYwam47OiFL2zZSPoG4Y+BVWFOg7xA6hYTYKKlJOU66oJZ0rr/rtTS0CHgpReuxs/jmVQCPywI0JxztlC34SDKZapGgWN02H+RaB963ffxHreDzGzwteC3bNtBSTvZZOgkIehjzxkLBqRH5y7WIswWqmmPD/FNIfnFsUMS7bDYKRxBgSqFEVwKOm5T5i/WKELROxl/W7R06RW+g+KJXZnTIYdNVzqh9ONBprjVUEqVj0zPQd1660TIZB7GIaXkAv9+2+mADy3Y2gcgoVTp6E0dCCgOg5g1pe8WH4wbQNkLQHhHjMEpJ8DBY0RvjK8osg27HvduACCYjNcH0zJi90W1Ayd7DyVkbGtzAiswwAsfBeHELCchhnqM+OuRqkIXoD2noEL8LxvvY3GPCTh2B2yYnq7sx3ITH75bo84t/v210uVl2ybBf5wBfhDNAvrpqXeUUN70O1Gwt0w8OnAOhIUa8ShCiwio7dZXW/X/YsuUOVNDlVevA1uBCp+4Qz5HPd1NlyamucCrA53KDykgcXu5Rc+c3VI9g5KymhspE3m//UR2FptBumw+3mV5j0M+KlGHBHZ+ikM7/dj32sMP7EYKBcFqsUP2YCFncu681igWLQcTx8KWUZPzE62awNtLcru6bEDLfrQvJ+m8CpQ3X4q3tzD0PqogXYt5XjFB1cmYPXIvPtvKCw9k+onCBopfWT8ZkjksLzV+aYadqSk9KgsX3gx/k2/pD/LpPs8LgMr/QUZXfprJ2Bh3sK+123P6/JWIVWK0utgzg2ozPkhNL6UgWVemjemAJU8VwT7XjcMHQ8h8eNqqFEIlaKeyB9ngb0FD/ByyN1baXm9fgh9A3ltd26FRWysnVqcHWJW/OR9kNJ/GoLU4vBcBilGzGBil8/iAhkFpEMpIPXhrLBMsbJBzBhqKLAmcjN17HsNXXpHjdu8+mwl9P9apk9eKRR+0p9szvU6HlnK2G1xGVi9vXqvoUZk4oUA140w8KW2B2hGARfWDThYP++x1tckmNjNo9dK/ONWKdN3UeKa+fTxRtecC+pXbZkPcH0PqsPU5GdX5v8DZakMZgg0cZrkXYfC4Yf7bkkn/x3idMf775eAigBEXGFYcgo3U79EQtXQ7cSCKXX6CxIMunoo3KUOPVwtaXIZWH3qfi5Yu4YakQk/go3A51XafUi4U+pTQweWGSpIZWD1pwtVrwtS+iUT1ko8/nFVJaKtZKENNQyFMnF/6l7Ye2wrCIc62GukDKz+drSR0l8z6TG4s5yIPXbew9BIXXqKI7oYbgOCvsbeepl7vWneY/JfDMqH4bhEMRj2hAXjZxCsp8vAcl5J/34NuK4av1OsTpjPte+GQa5jqNG/ErvuUjDhVbmY6bqQ6cgFU6ZLRUyzEdRnbFIeKANroB1vloAiH31VAlEsAbVsd6T0/bZw8tQoJ1Qhvy8m99ysPZxs2d8mVujnZWB5XmO/D7kEREONyEcPyMkTTsEuFX+ERir2boRdZSGDUrVCPqAPZYMFCNbX84Yluy+qe7MMrD70XdasxlCDAtVrJv4vWP+sgadkDIsElzI9uN0gIdfYPNGlXOnFmMsljdFeLNAHmxorE/v33bv7oqalaK4/rWhL2k8DeThFEcaf1ZKJ0+HY4wegXOS5YAuI31zx5OJI8cib9L5UImpojgdS2QHfS0DFrFA4GKxrrEjs3/9fuy8e9DktdbFaQgz4AeUCvD2g5nOuO6VrJv0fDItL4E4JogjVsOUScm7BYMfEB93FC+b/oIuFTEiAcMYx+wsGahvDyVjnq4nmfReDUn1RH6pGKlayPBQW4hUYO0qamV0z8WpYMV0KhUH0tY4Q+ZfSG/AZnBUmTuDTiFsVNcFgbWPIjna8YLfun1H38uvHgVLdnvJN5rbdF9OJQrxbn5RJf1Yw1IDqzZVPfhEbiP5aN1BLUkG9F0MNBQt+UiBxj71rhdCaHno07QoLt1YIgGA7M9BbGC1zM5Bk+4GnwUIt3XNxw69cKixnPmyHNk121Yvcji9TrMIiEEtA6HAqDV4z8U5I6T+hzmyD3GW9Hx5vCDIGAzZzrIn5/eFwhxITVlVdKFBVF7Q7W59IdLSev/viprF7vt74S4JqEtsFlKWDijUpU6z8vo+eS0sZamyBlN7aBEONejXUsGCoYQDjvVvT8GNAZGaEXoqVUvTLH8VS/snCnmvVDQFa/tjR1sdsO3AdwHS/Wz0LgAptnTmJZkGZaq7ZysDyvsxCHxtwLdoxCiPMn2AgewQc2mb2eJMJWAQV0xHzlXZpkA4YWCiNQ5kdCtQ0WuCf4NmscwOmsd/f8/VBD2u3gDKduUmCmahTpm4rAytTrxQyLWW/uGUoKNejMNQ4CTr13cFF8GggenDgpVY89wLr6BHgsQ7DOjidTff5dbI0DnlhAEqSHdjNNh5fiw3Llu75+pC/IB0q2aqoAHcE3XkovZblp881yVJW+VKuPWDcKV36QqU0qJT+dPiN6AIXX7cGHPA4G7COGiF1AFaffDfQsw55KMsCoBok2bqfT7sTz7l+zzcGbdNHc+uVZzZh8jGZwOtzKAOrz12Wpxu8LgkWbr0XUvopEKimGWp4gEUJmAGZl2L1DVgsAYAKhAO19ZJs3o9j+Zkkg8v3XFL/rLaM9frwmbZulj6AppaBNYDOG/CtpApcAmJYuPUnMNS4SNr2gmO2HP/0TDdgMjEBRmDxkjLvOVEs3pWA92hQqDpJtuxrR7k/hl/AFS9f3PQirolAZCBvI9XURxP7/1MGVv/7Lj93upJqLWzh1qWQ0s/pcqdECT6umKGQxwAV05R5j9L8C8A6vMehUAFlBcNhq7oGgNp/wLKTt8bDVatevbDmNT6TIoOtg15MpvyFMTEPoQysPHTigIugTzG5Gowy9OoXPPEdMPTXwykwAUVqxi2KFUwKMgUWKRYYpWhchn/wGGCxAU5BkLWLeQegrATW8cJWVTUB9Q6YqlsqrI4f/P3iEW+zvpNus8Nb3wAVM6sEA25E9wLKwOreHyU8gycf405pwdavwZ3S7ZIAP5+EOyUbUvoUuFxQxeLY76FSjjj+WC+giK6kFa4IWxVVYMr3vQ5ErgzG965+ceZxyqErhXpjUsEAZTqwDCzTE36JzYxx/tZzYFS2DkpzdMHZiRcF7866L5DjFhJE7IhRx8CNfi135COg7EBFdQig4izvJSwdLU80t/7k1SuOamfTCk2h0ruvDKz0HvHDuRGkLtx2OpistXZlw1C7+T2w3xBWwV9yuKpKhh0xApuIYJ+gZDJgVdUGaMQAQD0HfN2w59Wmnxu5kwOoxaBQEfJbRQtlYBWtq/v4IJdyjbx9b1Pzu3uvxXB4QbCmqT4MnqmiBgYL4Mq4aZ7diQleLPokhKM37P5G453mKTrkzcCyi6OuY5KLFpeBVbSu7seDPLKuCffZw5rfjU6OJ2LjIQyFm2S7E2B7MRBI/OmlbwzdbEpXQPWyjmfyFjL+/4JPu45FLkyEAAAAAElFTkSuQmCC'); + background-size: 24px; + background-repeat: no-repeat; + background-position: left center; + padding-left: 36px; + font-size: 20px; + letter-spacing: -0.04rem; + font-weight: 400; + color: white; + text-decoration: none; +} + +.message-container { + flex-grow: 1; + display: flex; + align-items: center; + justify-content: center; + margin: 0 30px; +} + +.message { + font-weight: 300; + font-size: 1.4rem; +} + +body.error .message { + display: none; +} + +body.error .error-message { + display: block; +} + +.error-message { + display: none; + font-weight: 300; + font-size: 1.3rem; +} + +.error-text { + color: red; + font-size: 1rem; +} + +@font-face { + font-family: 'Segoe UI'; + src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.eot?#iefix") format("embedded-opentype"); + src: local("Segoe UI Light"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.woff2") format("woff2"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.svg#web") format("svg"); + font-weight: 200 +} + +@font-face { + font-family: 'Segoe UI'; + src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.eot?#iefix") format("embedded-opentype"); + src: local("Segoe UI Semilight"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.woff2") format("woff2"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.svg#web") format("svg"); + font-weight: 300 +} + +@font-face { + font-family: 'Segoe UI'; + src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.eot?#iefix") format("embedded-opentype"); + src: local("Segoe UI"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.woff2") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.svg#web") format("svg"); + font-weight: 400 +} + +@font-face { + font-family: 'Segoe UI'; + src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.eot?#iefix") format("embedded-opentype"); + src: local("Segoe UI Semibold"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.woff2") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.svg#web") format("svg"); + font-weight: 600 +} + +@font-face { + font-family: 'Segoe UI'; + src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.eot?#iefix") format("embedded-opentype"); + src: local("Segoe UI Bold"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.woff2") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.svg#web") format("svg"); + font-weight: 700 +} diff --git a/extensions/github-authentication/media/favicon.ico b/extensions/github-authentication/media/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..7d1a59f7bdac3916c4461727ee106d2f2e532663 GIT binary patch literal 34494 zcmdsA-IG+ubw3hTRIwAYl~huGNW@4MNRM0j2iQub^2}3S*>aW33i@Cb^o^t$AtBHQ z?DAsAWl0N`ZOI@QVEGatW-XO1@v%GNOFTHqT~+dAS7}%vReDfy^ZT8-=T1*g-;Wts z7Uvd+)7`gk_c`4?-KS5V(<^d^+$BRp0$h=Yz9sT6BJ#cOwd0%rT;v4ODwTHpedPP+ z?}`i$C-LPXU)>`zGSZGe_>Uq#{wI+KQ3kT2#CAH62+A4sIrW?jH`m`4U4Cm>qxG}O zNI&JAc|k^JUXa$QCndi8PNfZ#l}2l_xSW})jLlXho_$f`(@*33Nqp}B>8Ot>t34z& zYf_u7;oAgGKO^z{j!GBQACVd$-u;M7xCJy{l3H_v#3{Ula@I>@{@r`y`JV%b0ec^l zXz!yED?BF6`a@D_zAO`;zap^Usb^S`<;c7f?EV+wSf2EFGtci{XcX*M_M5U|hX%6s?4F7O5OoO@j&Kx{DmobuFQ z+3Cj!T>16)V;7p^Qn|22qVsP`OgQ_xw2q;lr^X~c_=Lm<)=6t>os3u+S9aoi2QF@v z=)zWs2@JEm!|P=7Fv{?t#xkyaaDVK=rL9uEI03rfkPTJ`@rO@I^$5mG3QvKSF=<@g zeSho%^RiB2|2g)IR6oR+F*x|R#8>OfyTJd(j~{J9ba(b zK*_bSHZaUIzGEKd)o<`E=NU{@nuYV1KA5v^_H*)Csa@Le?P&h(dnWsxe~M^1yA}N7)t3nN6EFiDyv9Dg1`!w`2YcfR$fgqeH=>J(y(!B0%f{>)`x8zXS7Q z064E=o^I~J9O=*1R*9Cq0cE@MwuLc&&pa!U&fT29J;3}OaUC$&`-n7HXIl0~8UFlb zX#wIOY>-LTZ|j1Ob)DcGPvCk1Tz^zryB?Ae&hJ<&TJCz`hR?pzUOPz3{4X&72Y_oJ z){!P4Ca{j(|B87BWBTmIuJy!SU!o@XG9I=3U!wc~@Ez;Gyk6&8wFhONz<4;jDGcW} zN!?$2SjH8sUm@T-d$0~pp1@de*D^PJesiZi>>I;Z-oyG50DDh-{&kr+_ZrIgU>5Ck z80!|sK+VnXhOfMj@&mv;SVx9XcK!StSPMNkiE(%U{W1kO@NW_yT8Hr!kXzif)eY-9 z`=&H)opteV)=Rt}eWK-JjyYuTB*yeuXYD18S9W6k3;@>O209i^?yQ ze^hpX43c6Emy%;b^?bBmCL!Czkdxvgl#f#R#+7qA@=B5Z#NR-6x(t{TiKa!C)6omfDhO znOoWhhM6WZt$CPt5>bAuWw0!kiLxn&RRAM^R1O>cx!1_UhUHI7?@JC1(RQXL zqpA<6Lhg0o4C}{Q6p~Hdv{0F4(`Gz7RvN>z%2GT>7K#%4#Y0J+HP-_=RTpmgPUe~p|7ffj+mhP zi8ZJja0Y$r>A!m7*jsR~67W^%Fzht6JSt*!!`)QMQ8ZYb4~Zy3!{0sIl(vxW{wA*$1{j*H~Bm$$n4i zFkP+DfVk;;eN5di+JD5#V*8?=M$cXlbognYZNmD))eVQjUu>1N7ssJ%?uKU8z7BiE z$s^d476ICdd+my=TMmWEANHrKu}^K~?NzNEhU0bALO?KG&_EVd~P|{jM8k{nW2mJz3ZdTv<@&-vuXoz(04%z!C#=s%!2YUAJ zH*X*B)7gN$_YwAAA3^p$hW*!Jt3TP}FT_4Si-uHxu|WC-zg{yhj^a-23hW_9(a!=oZ|L%q&bSbnC%VZ8> z&t;ta5Ey0}-!TvK@*953GFXStIA@-sb^&L z_nVeBzxwIlM>q?rfu5n#H2nnhFCy?vi#iFO*~~ostr+L_)Qz-C^YJs#zes?yvI*)l z&Vc^Yp#LO(*QVDotoj}3nKIjW5oqV01iF|y^)b|kkO!q7^RlNCN_8`T{OPiHdCu6= z*0*^4llIx?rK$QP>T>|psZgiz8QVWguVm@eFFAb7gZ^6upuK|g$+j*mq1~ll$4|aq z%JfsFCmVtuOKt^_cIa**p0B3*v?Q#0MYR7Z>NU&LAEVtTq4Sc32Bp0!kx#e9*#{gy zsuuQ2L-k?kANElYFkXhBt8NB$$gVy)BMg2c$Z;b8j)fi?^U^Z) z+@|kNFwOzS0OA8^uYm3-lXmK#Lv(l28!Q9Bvx`E#xUVaRKDupJX*zO`Zq=Q4($qG< z#**sKH-h$nzPwBt?QHKVi$KpJXm_+mqMbj4jV*Qmq#e5G0YGg< z@q6L9Nf)>}_)_6t-N)bM2+jvuDG$)E*m(gl`+&dgJM~Li^=H2%W{`ro9i^Jpd0wpuK5hMrn8KtpjH^ z!80AS^>|Tx`|m?GZMkV4?Zyjvz;Ctw3D$N0oTo6pSz0q2}b|{otIvUX7{`;L_6kooOLzx&bvbIK86el+H|hkhdB%9 zmJcb12<#h7-SJXeN2tVn{EG!GWfUHx6+q{XP0VkYINJ#p@$FR{0ty@5V9G~O#|&b z&jYHQ{rk~Z{Xxp9@YaQFU=j_v?KY8D%!xju6At*W*_1~Y{BBOcd zq=9$pyTUOI`6BAF#dXe3?HrY7s1_#HIDBQ`BQgRIA5!|<^Hn=n4dC-z(e7K@_O?YB zwzxZZ{t2LstH1`j0(v6SG$7RQowyJ>i_J^BNPA(2em~530##t~fi|+ROK$pS#bx8L zH*T;DfDl49$7ve-#O#ujGw(}VamG8|bI$CZJ4YAB@!LjwX93325YC?)MQ6`h@4;(? zHDZM8MCdF!H*L>yIX^BV7m{;oo>d!w-hJ+Pv2l}y^$Z>BK+x_wFCDTWeXg*tc)aU8 zn?78rjW+C^L-G1@!5J{yb{KR=Mmuf80X&263vj+W&NJR1;OsM$&VC{NTt~X-b!pEE zC-jpCwCfP~811LcLw$jB`YwMq{|w)cv%d~ut)3$Np5I$xd@tI60U*-`0DX|!127CA z{q(t;g3MG%ug~8C&~EKP8-kQp=$Es?98;wK0O=3d^yjASL;Eiv9D7ywz9W_q(v5HVm*G_vyAc`-MK2>bKGH*-U&Ny8Jl(v=)3UA;>H| z;OxJG@f5*VBkqAN`ZH`oLbf9H74=&|yVYSlm@SE6+H`zKISD$0Y`YS(FKOdz`_->w z$jZJ|7(5Jt?k4?A3^ueU=o|Z>gwl zJ#(5xi8kN-4t%2ivhjyq_cqNhs%)FacwVMm5*|9716`Muiod4W5#pEW6a_p-=76UY zTuU$!(yu2NhDmt-JBE=Gp5M@c3Qw&%r&v~E@H4cmZJ#86Ca`tVN6n#XaMNOZ8ZKMJ zbI3^ySV@dhwZw1ajxKEJme0PMPRgkz<)R3!C#~1$Na!*;jc(&3rb8K?4xeZ&jmNbF zPbYXT!Paipj@GW$&f4y@(;5NP0I8i86TGLiJ=86>XUU@L(`TgeU$1?4@_#1&W)?P4 zCt$PEpIz5&!bcW=dFi~vWoG@IU(qIvcM<%@rlGc7*jvlK`v2Yzu;W+-d#10{CJu5n z?|L}>#GSrfS^w>@h&Fziwr?IyI6of4IsSFpbE!=o`A7eM2D!xS={#J%9TvgPuE$m` zhlbP7OAR&v*Sl=;u%@Q`zXO{j0`L7uF~hW%tX!XeyYdeGSu@-EHl7b?1FC$}1|k8q z=}Y+kl>Kk~=i}>Wi#@J=U)l%nV2^Vf0&PfLTSg6|4U~WEE9fh#<3FEozkRO5PVEHd z*b$p&|M1o_xt###$-& zVR|Qy*&(Jlu?PUFcw8gYG;J8r!DFgIrSK8B7 z+bdBS+7>#G@=iO{YTFMv6XRS=`NwZSWgzem+HMSXEUcYskqvBO#~P_EsPSJ*Y(p(h z%rs!-A9BE{L6(myua@#=Jm+EZPn%xOfmj1myVxC${VT_V8@I4K zPApCF;_#0#KMF|gXxRpS+)v|s&I4QrI{d?~uwUC+H@{qu+>-F;| zcFM3t6}{8Ku~ohid*zUQb7>o9wuNg4Y+i?Alp#xkjs0U|D5|` zGHlyBw(z+B_))2=Ek4e82f!`dQ8@+>1Kvjo`de!Iek1(Tem)s1uq_`0NC-N-lxT;* z-8$?;*aSzZjXc_+Oqk<=YX#(lc;`bhF*%C9*(jr_jl9{)lMg4%_ZBxC`X)*18Sg#) z@7T=uYh$nZ+cx%{yGO}?V&kptz_0^4MiSoD2K*uL|3ke?^vfJRI=%y5Tfj~_`0m_B z+}rWE{1rg*U?85|HUs~#__8TGp==a?T?;k-9Q$9-i8x-X|5C0W_ zlz}`Sgfj2U&%!X;U>uP8SyqtrY5hW7yjWDv;p0rBq259ZDAv!K5P@6FY5@9+*=#&`xY zN?Bi#f_%}ZugE_DWKih%knryU*xkulVS7)Uq4 z*YA$yLBh(j>$kyh#@%TdJ|I1QLgd?pI{fO8~hU5ah`p4|#EqSD$y#zY2ewqLuo} zyqRkqgzWr)?eQmn>+U1{j3uf`tPkIBoTCN$C znbeO6V*u+5ZL1^xZ4rPB3OCHZ<0IwzOTqUg_kO`HKdLt{CIT=Ed62euI(MSa>H9@_ z%JHF6{}|qh2#`S_^+B?Jb^bO>`i^OQZ|^MJ#$A45Y=fJ^JN>UHD_WnEZ}d6uV$?ek zVZY(}Vj;b+I|WSNkn&`_^@|ffE&6H^c(-F2AFaGR@PUf=V7#jj7uq1mH}(wld$PMB z72^&<_;3y9`EmL0+CE&?xA0@>;@{%L243ETZ}m~&-IDO(8Wi}qhmJ!h%R6Z4ySAUd zbEm&c`hl@s#sE?VQopKz4^_!`ZU2m|(hT^7wf~+1+hj{2MD$Z&pDW+VShq2r@{aaN z!^s%XxR;LaO6iF~Ro$M<6ZF~A*? z!{|HSt2(Ifo&Q~nf1d*&A;|N=EAuYMUx;Tf4gPxSFB$SvQolQ#O_cT3(|i~cnfWHBMT`)O7# zZiBDsHjDxGKk%UbrXl}S@HFoGXcorVX4pR|u-10J>gy-uI$n5}S+kpL#zo+2v--lnc=PQkVkFH?ezg>X- zyYK;Tn*VeNv;TDtr2b6%e{lZM?_*#7@*cUJfNcQ(>w1WH{T&$plmVbqEb`0FdXGLI zXTKNu;V#yD{kw{=^HpDT*!mgPIe>EDfc(=>9sPZu_4#7)SEgR`p(gL<)1LBA8G!xo z__C*c%{2Qv7RmeD!9TwY&wd_j1NG6s60up0cg*#F8u*1+RW-)VOWD4yTH{L8+_x#w$|=_h#` z%x!#IJD{Cd72C_&$=VIIX}W%h_h~!NCF!6| b`vD(nzu?1mM<)H2_TzNYuX9O1qu>7zZLY>L literal 0 HcmV?d00001 diff --git a/extensions/github-authentication/media/icon.png b/extensions/github-authentication/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c179f87a7119eed57e5780296b957325e1a93bbe GIT binary patch literal 3818 zcmZ`+X*kq<_y5g;v5$SoFxj(=u`gkUtYzyaDaE92q%c{>{u@h(GKI3mkVI69B4Wl8 zWlsoM1_^fw*@byL_w(xi;(xAlozFR+bDeXYbFS~3Z;GS66)%?r7XSdfHrD2U0RZIC zg#ZZHAzZ6;bv}e?M_Xr$Lj=G+Afgip@BXii=mH|Tfv6rJ@-X~YqyA<7=ZALzU%*iD;4+5@`J=?jDbnj1uyKysw)kRrx$pbN^41=U#d0heFYNg_ z^=o_T*A9L1Hz9w7kT;4+{T$mk=lE!x*7Gy{{X+Wt#el=wci#_t;xdLjDyIs2e)NCe zB-PB~GKPW3E+DF#R5JsRyZgRxQr|E9mx<`98d|3H{J>=n17K&>@bbTlbOGcJVkPr1 zTQqq6=HZT~g8#C20+2XIB1kQyPq7Gn%P&Zn*r|Z@eviZH{y(ApFF|r-QCY02!ovv1 zqE%s7#7J3N#lzeB*_fL;lOf-}n1;4uc;Nj2oJb)4)ZW7T>P>9DexugDqldZ*cFg*= z8?tyb9_MSte9L#bHNBXslm1n6ILVcbP8}^puDmUFz@2R&)c z(5Iio2iA=_@7&!pdk`Zhsl1WsyQL3Y<3A~wFJHe?p{_qv-*X{;fJAXET{6Mm+e=uD zZclXSllF*(@2+lG(PB4utcXIwI!jic-qBQ_-XRK=xitp<*?4wR9+lnR@5&NWc!hQT zF|XcSOph57ydKACb2jGBMq{`9MxSFzEYDLDX9LD5L}x zoUwmDI@B; zVo50LOg?AE&R^_88B`d1Tg>*LpYU2dfGm2^!Xd`Hlp^zzHPG`jD4Y!1bj=J}_= z%i;RmttRyAt8IvCTz9(#jE7$LOq zk&%wqquuMh;3com9E8(VBn|mgJycL$F;j)bHszed72sLOxh4jDQJY-Tiyhf8PEUN` zkjz{zw(f7%QDzDUyD;FNH~fT|Tx5uqAFZi zT?kRLIyO4Aq0Io)Vo(dYUFp}xp6+oxtzSJsGWI|;3_3HdQv^ojqBPr9!^0vqCRDAX z!4CvmJGRh!!d2&URB^go4LNk~+VPkETbydW5GsUn3J@}?_VD$Rk(ZN0pg2}g#L;2? z0vUJCG?7s`rB!`E14EK!B#Lct&llU#l9S9@D5vkiR6{;rc0fK)xTx-3cel*;Lt??R zK1f`hhff>`pHp*c-T)jqTz}CFYPg?_@eY7tlVnh1^Kh?@Fa>@>F{R8jO&l!Y$pTY3_*GN1eP9gjVHk>s)r>>)u~WILbiw ze7xW7PjDSB!}*8_c5-T|Yd;_)% zLb-^rIO!_uZn!ZXA2ML;!*M%qo}dxGS@00Vp06{li~s%CtH>E)5Oa02D5ARhHTUlU z2%1+Qtt^*^C{x9K1d|*}Jtc2>Oz;zkz!CwKm6J;u`bhI|bGTN~gxYIeSXp)pMCCX% zQG(FSzMWlN&AY^neAmhg2v%o(%w8sOtXO)2o*C>&60JFX+VV8)?Yw?YY zKcH>w_c-#MKV;IJ`xYc0T8Gd?vyWitzo8r>Bw^~udOmj<7(=}13amuYEH0=;tty6& zlwbybF0W!nWCWy<1y8Bi9jVum0LmJ~1~_qF`08&hF+CC3B%MEXLZ(OaT8E~iZ|&^^ zmA~%9$*x4N2nU6aJDS0w%$(MpQc8ttZbrVSv8=I4&?7iG=aN+O>8ZP!8c}EO83_9f zZJ3RT#&T#g#SToympI3E>j+jIk89s|_+TaP{-hMAEa8!g{XM2}$@ig`7a@10e1E&D z1646|{c6d^i{HzdXYA&O>km|-f}EoqhcDUGHvi&-#T;w3jv~m!eWlj_hIW54&QvOt ztxEUj=O7MZ#a7|QZ)ET?Yn&$aC2Q)^S`py5B~v2gWE>}AG3$Hr9(7P3Je3B77eHAB1?&}qTi zhgL?Pg7Hh#ntD>irC^)Q71fn_8HP-@f}FJw200(1-p?t+HoHC0!*Ir!bi2iJ?E7+5 zaeaV^%K#T$o2=0_2jed-|LIR1H;bZJao(>;@b0W!W|NRu=i=~L4GWWcl0U7bvQ^vo z=PA;;fcAHkfWV|&cSdxBHZmI{iEhm+cLMkrR4|qpObvWi?Y8-Pz|s!yaTL~J^T5e! z1tTxP3DY%XU!h3keQ!l6gk}a;*S1-90HO<0_s?f&UyjZU&7M$A0hL%Bi^64(7b#In zh#5)TF9=$D#0mB;?NyY|?1bE%%yIkUJr}8kX&Q#?H8o)o*?@?Vv+z6^8n2kcQ$<%p zu*qL8QDguKu&2@&7SnFI@R`p*PYhNq#wVr=(p~Lh#O`Zenm>H^C-lT8M^R|GS3xJ6 z#2g>uf@bs`ucHz*K%!#S!!PD2kSqC^9vVH!6Vsb#w0x?qA*o1>b8OTxVvJH3xH3;Q z1epN$>A_jq=eBW0W(v4ug%}?%H^_p|unNB&$Q`VJ`%7)-Yu=;vTw~kEM*#zZ^of&C zE##@ppg`(oo%SQ?_{Zl17~Y|%2@mq4^9)LG!O~om&qufk}$6-P`$&l@s z3lXInqDF6SO2zXiYfVCbh|z2H?4N(3$u`9GpyF~`aJ{UVQlbjC#RI>6JZHgpn_7Q) zcmDyOzxFQxDn?Kvm7j4p`uO^wkm?-A`MdcXFeA+b+Smx(fCA8)qp&{4U+Xe`I&k?GCjT4RUwrhMNG3%%J>&ccUzAl4 z%KOR=&rWD0LUUeFbyy$RD!cX%z8sHllxk^j zI+;+u*GhV+Z0~@O?OwfldPvBD5mU^g2xRjxjWDE)k8--K?Yy-L(uqcCMZg~V4s50< zX6i!*YzR}1Z4|G+^dX3*{$Qnt2qn)aflN>kuuB3^M~ry%Je1g}jkpp8Mh=rU)L~y? zOMWTe;d6HGg!*erYNE^N%4eTc=mUu4#UZkS!kOIwelDO{FH5|>Vus6sWXCpqhs>Mc z4MU1X8|dq{>G)mU$>q2L0DMDZq^5<4YpJhPD_>8{>@ClI>A2B`<6-OPj&9Culx zVmkcx4Hnx@`y-*uDl$4shF3i=rynI+FIXO-cU#lA=pHx~_J5^oG9d=n#|zJ9ZGZN3oU<~jdk3i~iZ7@lksY8v{t1-@_jk;UGOAX&{83PJn$YV7uw=eSV?`On9)*hS-dHaVw=f9s;8w-2$=VrvX{{Z7E B3MBvl literal 0 HcmV?d00001 diff --git a/extensions/github-authentication/media/index.html b/extensions/github-authentication/media/index.html new file mode 100644 index 00000000000..9c0a9eec080 --- /dev/null +++ b/extensions/github-authentication/media/index.html @@ -0,0 +1,37 @@ + + + + + + + Azure Account - Sign In + + + + + + + Visual Studio Code + +
+
+ You are signed in now and can close this page. +
+
+ An error occurred while signing in: +
+
+
+ + + + diff --git a/extensions/github-authentication/src/authServer.ts b/extensions/github-authentication/src/authServer.ts new file mode 100644 index 00000000000..de08c6fca0f --- /dev/null +++ b/extensions/github-authentication/src/authServer.ts @@ -0,0 +1,198 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as http from 'http'; +import { URL } from 'url'; +import * as fs from 'fs'; +import * as path from 'path'; +import { randomBytes } from 'crypto'; + +function sendFile(res: http.ServerResponse, filepath: string) { + fs.readFile(filepath, (err, body) => { + if (err) { + console.error(err); + res.writeHead(404); + res.end(); + } else { + res.writeHead(200, { + 'content-length': body.length, + }); + res.end(body); + } + }); +} + +interface IOAuthResult { + code: string; + state: string; +} + +interface ILoopbackServer { + /** + * If undefined, the server is not started yet. + */ + port: number | undefined; + + /** + * The nonce used + */ + nonce: string; + + /** + * The state parameter used in the OAuth flow. + */ + state: string | undefined; + + /** + * Starts the server. + * @returns The port to listen on. + * @throws If the server fails to start. + * @throws If the server is already started. + */ + start(): Promise; + /** + * Stops the server. + * @throws If the server is not started. + * @throws If the server fails to stop. + */ + stop(): Promise; + /** + * Returns a promise that resolves to the result of the OAuth flow. + */ + waitForOAuthResponse(): Promise; +} + +export class LoopbackAuthServer implements ILoopbackServer { + private readonly _server: http.Server; + private readonly _resultPromise: Promise; + private _startingRedirect: URL; + + public nonce = randomBytes(16).toString('base64'); + public port: number | undefined; + + public set state(state: string | undefined) { + if (state) { + this._startingRedirect.searchParams.set('state', state); + } else { + this._startingRedirect.searchParams.delete('state'); + } + } + public get state(): string | undefined { + return this._startingRedirect.searchParams.get('state') ?? undefined; + } + + constructor(serveRoot: string, startingRedirect: string) { + if (!serveRoot) { + throw new Error('serveRoot must be defined'); + } + if (!startingRedirect) { + throw new Error('startingRedirect must be defined'); + } + this._startingRedirect = new URL(startingRedirect); + let deferred: { resolve: (result: IOAuthResult) => void; reject: (reason: any) => void }; + this._resultPromise = new Promise((resolve, reject) => deferred = { resolve, reject }); + + this._server = http.createServer((req, res) => { + const reqUrl = new URL(req.url!, `http://${req.headers.host}`); + switch (reqUrl.pathname) { + case '/signin': { + const receivedNonce = (reqUrl.searchParams.get('nonce') ?? '').replace(/ /g, '+'); + if (receivedNonce !== this.nonce) { + res.writeHead(302, { location: `/?error=${encodeURIComponent('Nonce does not match.')}` }); + res.end(); + } + res.writeHead(302, { location: this._startingRedirect.toString() }); + res.end(); + break; + } + case '/callback': { + const code = reqUrl.searchParams.get('code') ?? undefined; + const state = reqUrl.searchParams.get('state') ?? undefined; + const nonce = (reqUrl.searchParams.get('nonce') ?? '').replace(/ /g, '+'); + if (!code || !state || !nonce) { + res.writeHead(400); + res.end(); + return; + } + if (this.state !== state) { + res.writeHead(302, { location: `/?error=${encodeURIComponent('State does not match.')}` }); + res.end(); + throw new Error('State does not match.'); + } + if (this.nonce !== nonce) { + res.writeHead(302, { location: `/?error=${encodeURIComponent('Nonce does not match.')}` }); + res.end(); + throw new Error('Nonce does not match.'); + } + deferred.resolve({ code, state }); + res.writeHead(302, { location: '/' }); + res.end(); + break; + } + // Serve the static files + case '/': + sendFile(res, path.join(serveRoot, 'index.html')); + break; + default: + // substring to get rid of leading '/' + sendFile(res, path.join(serveRoot, reqUrl.pathname.substring(1))); + break; + } + }); + } + + public start(): Promise { + return new Promise((resolve, reject) => { + if (this._server.listening) { + throw new Error('Server is already started'); + } + const portTimeout = setTimeout(() => { + reject(new Error('Timeout waiting for port')); + }, 5000); + this._server.on('listening', () => { + const address = this._server.address(); + if (typeof address === 'string') { + this.port = parseInt(address); + } else if (address instanceof Object) { + this.port = address.port; + } else { + throw new Error('Unable to determine port'); + } + + clearTimeout(portTimeout); + + // set state which will be used to redirect back to vscode + this.state = `http://127.0.0.1:${this.port}/callback?nonce=${encodeURIComponent(this.nonce)}`; + + resolve(this.port); + }); + this._server.on('error', err => { + reject(new Error(`Error listening to server: ${err}`)); + }); + this._server.on('close', () => { + reject(new Error('Closed')); + }); + this._server.listen(0, '127.0.0.1'); + }); + } + + public stop(): Promise { + return new Promise((resolve, reject) => { + if (!this._server.listening) { + throw new Error('Server is not started'); + } + this._server.close((err) => { + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); + } + + public waitForOAuthResponse(): Promise { + return this._resultPromise; + } +} diff --git a/extensions/github-authentication/src/env/browser/authServer.ts b/extensions/github-authentication/src/env/browser/authServer.ts new file mode 100644 index 00000000000..60b53c713a8 --- /dev/null +++ b/extensions/github-authentication/src/env/browser/authServer.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export function startServer(_: any): any { + throw new Error('Not implemented'); +} + +export function createServer(_: any): any { + throw new Error('Not implemented'); +} diff --git a/extensions/github-authentication/src/github.ts b/extensions/github-authentication/src/github.ts index 266a8a098d7..9deaef3000b 100644 --- a/extensions/github-authentication/src/github.ts +++ b/extensions/github-authentication/src/github.ts @@ -45,10 +45,8 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid if (this.type === AuthProviderType.github) { this._githubServer = new GitHubServer( - // We only can use the Device Code flow when we are running with a remote extension host. - context.extension.extensionKind === vscode.ExtensionKind.Workspace - // This should only matter when we are running in code-oss. See the other change in this commit. - || vscode.env.uiKind === vscode.UIKind.Desktop, + // We only can use the Device Code flow when we have a full node environment because of CORS. + context.extension.extensionKind === vscode.ExtensionKind.Workspace || vscode.env.uiKind === vscode.UIKind.Desktop, this._logger, this._telemetryReporter); } else { diff --git a/extensions/github-authentication/src/githubServer.ts b/extensions/github-authentication/src/githubServer.ts index bcc8ca4e5ea..50f8983d368 100644 --- a/extensions/github-authentication/src/githubServer.ts +++ b/extensions/github-authentication/src/githubServer.ts @@ -12,6 +12,8 @@ import { ExperimentationTelemetry } from './experimentationService'; import { AuthProviderType } from './github'; import { Log } from './common/logger'; import { isSupportedEnvironment } from './common/env'; +import { LoopbackAuthServer } from './authServer'; +import path = require('path'); const localize = nls.loadMessageBundle(); const CLIENT_ID = '01ab8ac9400c4e429b23'; @@ -110,15 +112,24 @@ async function getUserInfo(token: string, serverUri: vscode.Uri, logger: Log): P export class GitHubServer implements IGitHubServer { friendlyName = 'GitHub'; type = AuthProviderType.github; - private _onDidManuallyProvideToken = new vscode.EventEmitter(); private _pendingNonces = new Map(); private _codeExchangePromises = new Map; cancel: vscode.EventEmitter }>(); private _disposable: vscode.Disposable; private _uriHandler = new UriEventHandler(this._logger); + private readonly getRedirectEndpoint: Thenable; constructor(private readonly _supportDeviceCodeFlow: boolean, private readonly _logger: Log, private readonly _telemetryReporter: ExperimentationTelemetry) { this._disposable = vscode.window.registerUriHandler(this._uriHandler); + + this.getRedirectEndpoint = vscode.commands.executeCommand<{ [providerId: string]: string } | undefined>('workbench.getCodeExchangeProxyEndpoints').then((proxyEndpoints) => { + // If we are running in insiders vscode.dev, then ensure we use the redirect route on that. + let redirectUri = REDIRECT_URL_STABLE; + if (proxyEndpoints?.github && new URL(proxyEndpoints.github).hostname === 'insiders.vscode.dev') { + redirectUri = REDIRECT_URL_INSIDERS; + } + return redirectUri; + }); } dispose() { @@ -134,84 +145,147 @@ export class GitHubServer implements IGitHubServer { public async login(scopes: string): Promise { this._logger.info(`Logging in for the following scopes: ${scopes}`); + // Used for showing a friendlier message to the user when the explicitly cancel a flow. + let userCancelled: boolean = false; + const yes = localize('yes', "Yes"); + const no = localize('no', "No"); + const getMessage = () => userCancelled + ? localize('userCancelledMessage', "Having trouble logging in? Would you like to try a different way?") + : localize('otherReasonMessage', "You have not yet finished authorizing this extension to use GitHub. Would you like to keep trying?"); + const nonce = uuid(); const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/did-authenticate?nonce=${encodeURIComponent(nonce)}`)); - if (!isSupportedEnvironment(callbackUri)) { - const token = this._supportDeviceCodeFlow - ? await this.doDeviceCodeFlow(scopes) - : await vscode.window.showInputBox({ prompt: 'GitHub Personal Access Token', ignoreFocusOut: true }); - - if (!token) { throw new Error('No token provided'); } - - const tokenScopes = await getScopes(token, this.getServerUri('/'), this._logger); // Example: ['repo', 'user'] - const scopesList = scopes.split(' '); // Example: 'read:user repo user:email' - if (!scopesList.every(scope => { - const included = tokenScopes.includes(scope); - if (included || !scope.includes(':')) { - return included; - } - - return scope.split(':').some(splitScopes => { - return tokenScopes.includes(splitScopes); - }); - })) { - throw new Error(`The provided token does not match the requested scopes: ${scopes}`); + const supported = isSupportedEnvironment(callbackUri); + if (supported) { + try { + return await this.doLoginWithoutLocalServer(scopes, nonce, callbackUri); + } catch (e) { + this._logger.error(e); + userCancelled = e.message ?? e === 'User Cancelled'; } - return token; + let choice = await vscode.window.showWarningMessage(getMessage(), yes, no); + if (choice !== yes) { + throw new Error('Cancelled'); + } } - const existingNonces = this._pendingNonces.get(scopes) || []; - this._pendingNonces.set(scopes, [...existingNonces, nonce]); + // Starting a local server isn't supported in web + if (vscode.env.uiKind === vscode.UIKind.Desktop) { + try { + return await this.doLoginWithLocalServer(scopes); + } catch (e) { + this._logger.error(e); + userCancelled = e.message ?? e === 'User Cancelled'; + } - const proxyEndpoints: { [providerId: string]: string } | undefined = await vscode.commands.executeCommand('workbench.getCodeExchangeProxyEndpoints'); - // If we are running in insiders vscode.dev, then ensure we use the redirect route on that. - let redirectUri = REDIRECT_URL_STABLE; - if (proxyEndpoints?.github && new URL(proxyEndpoints.github).hostname === 'insiders.vscode.dev') { - redirectUri = REDIRECT_URL_INSIDERS; + let choice = await vscode.window.showWarningMessage(getMessage(), yes, no); + if (choice !== yes) { + throw new Error('Cancelled'); + } } - const searchParams = new URLSearchParams([ - ['client_id', CLIENT_ID], - ['redirect_uri', redirectUri], - ['scope', scopes], - ['state', encodeURIComponent(callbackUri.toString(true))] - ]); - const uri = vscode.Uri.parse(`${GITHUB_AUTHORIZE_URL}?${searchParams.toString()}`); - return vscode.window.withProgress({ - location: vscode.ProgressLocation.Window, - title: localize('signingIn', " $(mark-github) Signing in to github.com..."), - }, async () => { + if (this._supportDeviceCodeFlow) { + try { + return await this.doLoginDeviceCodeFlow(scopes); + } catch (e) { + this._logger.error(e); + userCancelled = e.message ?? e === 'User Cancelled'; + } + } else { + try { + return await this.doLoginWithPat(scopes); + } catch (e) { + this._logger.error(e); + userCancelled = e.message ?? e === 'User Cancelled'; + } + } + + throw new Error(userCancelled ? 'Cancelled' : 'No auth flow succeeded.'); + } + + private async doLoginWithoutLocalServer(scopes: string, nonce: string, callbackUri: vscode.Uri): Promise { + this._logger.info(`Trying without local server... (${scopes})`); + return await vscode.window.withProgress({ + location: vscode.ProgressLocation.Notification, + title: localize('signingIn', "Signing in to github.com..."), + cancellable: true + }, async (_, token) => { + const existingNonces = this._pendingNonces.get(scopes) || []; + this._pendingNonces.set(scopes, [...existingNonces, nonce]); + const redirectUri = await this.getRedirectEndpoint; + const searchParams = new URLSearchParams([ + ['client_id', CLIENT_ID], + ['redirect_uri', redirectUri], + ['scope', scopes], + ['state', encodeURIComponent(callbackUri.toString(true))] + ]); + const uri = vscode.Uri.parse(`${GITHUB_AUTHORIZE_URL}?${searchParams.toString()}`); await vscode.env.openExternal(uri); // Register a single listener for the URI callback, in case the user starts the login process multiple times // before completing it. let codeExchangePromise = this._codeExchangePromises.get(scopes); if (!codeExchangePromise) { - codeExchangePromise = promiseFromEvent(this._uriHandler.event, this.exchangeCodeForToken(scopes)); + codeExchangePromise = promiseFromEvent(this._uriHandler.event, this.handleUri(scopes)); this._codeExchangePromises.set(scopes, codeExchangePromise); } - return Promise.race([ - codeExchangePromise.promise, - promiseFromEvent(this._onDidManuallyProvideToken.event, (token: string | undefined, resolve, reject): void => { - if (!token) { - reject('Cancelled'); - } else { - resolve(token); - } - }).promise, - new Promise((_, reject) => setTimeout(() => reject('Cancelled'), 60000)) - ]).finally(() => { + try { + return await Promise.race([ + codeExchangePromise.promise, + new Promise((_, reject) => setTimeout(() => reject('Cancelled'), 60000)), + promiseFromEvent(token.onCancellationRequested, (_, __, reject) => { reject('User Cancelled'); }).promise + ]); + } finally { this._pendingNonces.delete(scopes); codeExchangePromise?.cancel.fire(); this._codeExchangePromises.delete(scopes); - }); + } }); } - private async doDeviceCodeFlow(scopes: string): Promise { + private async doLoginWithLocalServer(scopes: string): Promise { + this._logger.info(`Trying with local server... (${scopes})`); + return await vscode.window.withProgress({ + location: vscode.ProgressLocation.Notification, + title: localize('signingInAnotherWay', "Signing in to github.com..."), + cancellable: true + }, async (_, token) => { + const redirectUri = await this.getRedirectEndpoint; + const searchParams = new URLSearchParams([ + ['client_id', CLIENT_ID], + ['redirect_uri', redirectUri], + ['scope', scopes], + ]); + const loginUrl = `${GITHUB_AUTHORIZE_URL}?${searchParams.toString()}`; + const server = new LoopbackAuthServer(path.join(__dirname, '../media'), loginUrl); + const port = await server.start(); + + let codeToExchange; + try { + vscode.env.openExternal(vscode.Uri.parse(`http://127.0.0.1:${port}/signin?nonce=${encodeURIComponent(server.nonce)}`)); + const { code } = await Promise.race([ + server.waitForOAuthResponse(), + new Promise((_, reject) => setTimeout(() => reject('Cancelled'), 60000)), + promiseFromEvent(token.onCancellationRequested, (_, __, reject) => { reject('User Cancelled'); }).promise + ]); + codeToExchange = code; + } finally { + setTimeout(() => { + void server.stop(); + }, 5000); + } + + const accessToken = await this.exchangeCodeForToken(codeToExchange); + return accessToken; + }); + } + + private async doLoginDeviceCodeFlow(scopes: string): Promise { + this._logger.info(`Trying device code flow... (${scopes})`); + // Get initial device code const uri = `https://github.com/login/device/code?client_id=${CLIENT_ID}&scope=${scopes}`; const result = await fetch(uri, { @@ -235,7 +309,7 @@ export class GitHubServer implements IGitHubServer { }, 'Copy & Continue to GitHub'); if (modalResult !== 'Copy & Continue to GitHub') { - throw new Error('Cancelled'); + throw new Error('User Cancelled'); } await vscode.env.clipboard.writeText(json.user_code); @@ -243,6 +317,35 @@ export class GitHubServer implements IGitHubServer { const uriToOpen = await vscode.env.asExternalUri(vscode.Uri.parse(json.verification_uri)); await vscode.env.openExternal(uriToOpen); + return await this.waitForDeviceCodeAccessToken(json); + } + + private async doLoginWithPat(scopes: string): Promise { + this._logger.info(`Trying to retrieve PAT... (${scopes})`); + const token = await vscode.window.showInputBox({ prompt: 'GitHub Personal Access Token', ignoreFocusOut: true }); + if (!token) { throw new Error('User Cancelled'); } + + const tokenScopes = await getScopes(token, this.getServerUri('/'), this._logger); // Example: ['repo', 'user'] + const scopesList = scopes.split(' '); // Example: 'read:user repo user:email' + if (!scopesList.every(scope => { + const included = tokenScopes.includes(scope); + if (included || !scope.includes(':')) { + return included; + } + + return scope.split(':').some(splitScopes => { + return tokenScopes.includes(splitScopes); + }); + })) { + throw new Error(`The provided token does not match the requested scopes: ${scopes}`); + } + + return token; + } + + private async waitForDeviceCodeAccessToken( + json: IGitHubDeviceCodeResponse, + ): Promise { return await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, cancellable: true, @@ -252,67 +355,63 @@ export class GitHubServer implements IGitHubServer { json.verification_uri, json.user_code) }, async (_, token) => { - return await this.waitForDeviceCodeAccessToken(json, token); + const refreshTokenUri = `https://github.com/login/oauth/access_token?client_id=${CLIENT_ID}&device_code=${json.device_code}&grant_type=urn:ietf:params:oauth:grant-type:device_code`; + + // Try for 2 minutes + const attempts = 120 / json.interval; + for (let i = 0; i < attempts; i++) { + await new Promise(resolve => setTimeout(resolve, json.interval * 1000)); + if (token.isCancellationRequested) { + throw new Error('User Cancelled'); + } + let accessTokenResult; + try { + accessTokenResult = await fetch(refreshTokenUri, { + method: 'POST', + headers: { + Accept: 'application/json' + } + }); + } catch { + continue; + } + + if (!accessTokenResult.ok) { + continue; + } + + const accessTokenJson = await accessTokenResult.json(); + + if (accessTokenJson.error === 'authorization_pending') { + continue; + } + + if (accessTokenJson.error) { + throw new Error(accessTokenJson.error_description); + } + + return accessTokenJson.access_token; + } + + throw new Error('Cancelled'); }); } - private async waitForDeviceCodeAccessToken( - json: IGitHubDeviceCodeResponse, - token: vscode.CancellationToken - ): Promise { - - const refreshTokenUri = `https://github.com/login/oauth/access_token?client_id=${CLIENT_ID}&device_code=${json.device_code}&grant_type=urn:ietf:params:oauth:grant-type:device_code`; - - // Try for 2 minutes - const attempts = 120 / json.interval; - for (let i = 0; i < attempts; i++) { - await new Promise(resolve => setTimeout(resolve, json.interval * 1000)); - if (token.isCancellationRequested) { - throw new Error('Cancelled'); - } - let accessTokenResult; - try { - accessTokenResult = await fetch(refreshTokenUri, { - method: 'POST', - headers: { - Accept: 'application/json' - } - }); - } catch { - continue; - } - - if (!accessTokenResult.ok) { - continue; - } - - const accessTokenJson = await accessTokenResult.json(); - - if (accessTokenJson.error === 'authorization_pending') { - continue; - } - - if (accessTokenJson.error) { - throw new Error(accessTokenJson.error_description); - } - - return accessTokenJson.access_token; - } - - throw new Error('Cancelled'); - } - - private exchangeCodeForToken: (scopes: string) => PromiseAdapter = - (scopes) => async (uri, resolve, reject) => { + private handleUri: (scopes: string) => PromiseAdapter = + (scopes) => (uri, resolve, reject) => { const query = new URLSearchParams(uri.query); const code = query.get('code'); - - const acceptedNonces = this._pendingNonces.get(scopes) || []; const nonce = query.get('nonce'); - if (!nonce) { - this._logger.error('No nonce in response.'); + if (!code) { + reject(new Error('No code')); return; } + if (!nonce) { + reject(new Error('No nonce')); + return; + } + + const acceptedNonces = this._pendingNonces.get(scopes) || []; if (!acceptedNonces.includes(nonce)) { // A common scenario of this happening is if you: // 1. Trigger a sign in with one set of scopes @@ -323,36 +422,39 @@ export class GitHubServer implements IGitHubServer { return; } - this._logger.info('Exchanging code for token...'); - - const proxyEndpoints: { [providerId: string]: string } | undefined = await vscode.commands.executeCommand('workbench.getCodeExchangeProxyEndpoints'); - const endpointUrl = proxyEndpoints?.github ? `${proxyEndpoints.github}login/oauth/access_token` : GITHUB_TOKEN_URL; - - try { - const body = `code=${code}`; - const result = await fetch(endpointUrl, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': body.toString() - - }, - body - }); - - if (result.ok) { - const json = await result.json(); - this._logger.info('Token exchange success!'); - resolve(json.access_token); - } else { - reject(result.statusText); - } - } catch (ex) { - reject(ex); - } + resolve(this.exchangeCodeForToken(code)); }; + private async exchangeCodeForToken(code: string): Promise { + this._logger.info('Exchanging code for token...'); + + const proxyEndpoints: { [providerId: string]: string } | undefined = await vscode.commands.executeCommand('workbench.getCodeExchangeProxyEndpoints'); + const endpointUrl = proxyEndpoints?.github ? `${proxyEndpoints.github}login/oauth/access_token` : GITHUB_TOKEN_URL; + + const body = `code=${code}`; + const result = await fetch(endpointUrl, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': body.toString() + + }, + body + }); + + if (result.ok) { + const json = await result.json(); + this._logger.info('Token exchange success!'); + return json.access_token; + } else { + const text = await result.text(); + const error = new Error(text); + error.name = 'GitHubTokenExchangeError'; + throw error; + } + } + private getServerUri(path: string = '') { const apiUri = vscode.Uri.parse('https://api.github.com'); return vscode.Uri.parse(`${apiUri.scheme}://${apiUri.authority}${path}`); From e6d98a13fcd3bf928d0e755eaae873ac200a98bd Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 16 Apr 2022 07:21:18 +0200 Subject: [PATCH 117/245] watcher - allow relative patterns for `include` (#146335) --- src/vs/base/common/glob.ts | 19 +++++++++++-- src/vs/base/test/common/glob.test.ts | 26 ++++++++++++++++++ src/vs/platform/files/common/files.ts | 4 +-- src/vs/platform/files/common/watcher.ts | 3 ++- .../node/watcher/nodejs/nodejsWatcher.ts | 6 ++--- .../node/watcher/parcel/parcelWatcher.ts | 27 ++++++++++--------- .../node/nodejsWatcher.integrationTest.ts | 6 +++++ .../node/parcelWatcher.integrationTest.ts | 6 +++++ .../browser/parts/editor/editorDropTarget.ts | 8 +++--- .../services/search/common/searchExtTypes.ts | 2 +- src/vscode-dts/vscode.d.ts | 2 +- 11 files changed, 81 insertions(+), 28 deletions(-) diff --git a/src/vs/base/common/glob.ts b/src/vs/base/common/glob.ts index 958eae2d839..0a5039769f5 100644 --- a/src/vs/base/common/glob.ts +++ b/src/vs/base/common/glob.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { equals } from 'vs/base/common/arrays'; import { isThenable } from 'vs/base/common/async'; import { CharCode } from 'vs/base/common/charCode'; import { isEqualOrParent } from 'vs/base/common/extpath'; @@ -491,7 +492,7 @@ function toRegExp(pattern: string): ParsedStringPattern { /** * Simplified glob matching. Supports a subset of glob patterns: - * * `*` to match one or more characters in a path segment + * * `*` to match zero or more characters in a path segment * * `?` to match on one character in a path segment * * `**` to match any number of path segments, including none * * `{}` to group conditions (e.g. *.{ts,js} matches all TypeScript and JavaScript files) @@ -510,7 +511,7 @@ export function match(arg1: string | IExpression | IRelativePattern, path: strin /** * Simplified glob matching. Supports a subset of glob patterns: - * * `*` to match one or more characters in a path segment + * * `*` to match zero or more characters in a path segment * * `?` to match on one character in a path segment * * `**` to match any number of path segments, including none * * `{}` to group conditions (e.g. *.{ts,js} matches all TypeScript and JavaScript files) @@ -797,3 +798,17 @@ function aggregateBasenameMatches(parsedPatterns: Array | undefined, patternsB: Array | undefined): boolean { + return equals(patternsA, patternsB, (a, b) => { + if (typeof a === 'string' && typeof b === 'string') { + return a === b; + } + + if (typeof a !== 'string' && typeof b !== 'string') { + return a.base === b.base && a.pattern === b.pattern; + } + + return false; + }); +} diff --git a/src/vs/base/test/common/glob.test.ts b/src/vs/base/test/common/glob.test.ts index bc70f0fa531..7963c4c4139 100644 --- a/src/vs/base/test/common/glob.test.ts +++ b/src/vs/base/test/common/glob.test.ts @@ -1066,6 +1066,18 @@ suite('Glob', () => { } }); + test('relative pattern - single star alone', function () { + if (isWindows) { + let p: glob.IRelativePattern = { base: 'C:\\DNXConsoleApp\\foo\\something\\Program.cs', pattern: '*' }; + assertGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\something\\Program.cs'); + assertNoGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\Program.cs'); + } else { + let p: glob.IRelativePattern = { base: '/DNXConsoleApp/foo/something/Program.cs', pattern: '*' }; + assertGlobMatch(p, '/DNXConsoleApp/foo/something/Program.cs'); + assertNoGlobMatch(p, '/DNXConsoleApp/foo/Program.cs'); + } + }); + test('relative pattern - ignores case on macOS/Windows', function () { if (isWindows) { let p: glob.IRelativePattern = { base: 'C:\\DNXConsoleApp\\foo', pattern: 'something/*.cs' }; @@ -1114,4 +1126,18 @@ suite('Glob', () => { assert.strictEqual('**/*.js', await parsedExpression('test.js', undefined, hasSibling)); }); + + test('patternsEquals', () => { + assert.ok(glob.patternsEquals(['a'], ['a'])); + assert.ok(!glob.patternsEquals(['a'], ['b'])); + + assert.ok(glob.patternsEquals(['a', 'b', 'c'], ['a', 'b', 'c'])); + assert.ok(!glob.patternsEquals(['1', '2'], ['1', '3'])); + + assert.ok(glob.patternsEquals([{ base: 'a', pattern: '*' }, 'b', 'c'], [{ base: 'a', pattern: '*' }, 'b', 'c'])); + + assert.ok(glob.patternsEquals(undefined, undefined)); + assert.ok(!glob.patternsEquals(undefined, ['b'])); + assert.ok(!glob.patternsEquals(['a'], undefined)); + }); }); diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index 028d33a315c..47e7c042f8f 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -7,7 +7,7 @@ import { VSBuffer, VSBufferReadable, VSBufferReadableStream } from 'vs/base/comm import { CancellationToken } from 'vs/base/common/cancellation'; import { ErrorNoTelemetry } from 'vs/base/common/errors'; import { Event } from 'vs/base/common/event'; -import { IExpression } from 'vs/base/common/glob'; +import { IExpression, IRelativePattern } from 'vs/base/common/glob'; import { IDisposable } from 'vs/base/common/lifecycle'; import { TernarySearchTree } from 'vs/base/common/map'; import { sep } from 'vs/base/common/path'; @@ -437,7 +437,7 @@ export interface IWatchOptions { * watching. If not provided, all paths are considered for * events. */ - includes?: string[]; + includes?: Array; } export const enum FileSystemProviderCapabilities { diff --git a/src/vs/platform/files/common/watcher.ts b/src/vs/platform/files/common/watcher.ts index 78614c324dc..5a2a7cfd786 100644 --- a/src/vs/platform/files/common/watcher.ts +++ b/src/vs/platform/files/common/watcher.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; +import { IRelativePattern } from 'vs/base/common/glob'; import { Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { isLinux } from 'vs/base/common/platform'; import { URI as uri } from 'vs/base/common/uri'; @@ -31,7 +32,7 @@ interface IWatchRequest { * watching. If not provided, all paths are considered for * events. */ - includes?: string[]; + includes?: Array; } export interface INonRecursiveWatchRequest extends IWatchRequest { diff --git a/src/vs/platform/files/node/watcher/nodejs/nodejsWatcher.ts b/src/vs/platform/files/node/watcher/nodejs/nodejsWatcher.ts index df6bd3f016c..a737ca54510 100644 --- a/src/vs/platform/files/node/watcher/nodejs/nodejsWatcher.ts +++ b/src/vs/platform/files/node/watcher/nodejs/nodejsWatcher.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { equals } from 'vs/base/common/arrays'; import { Event, Emitter } from 'vs/base/common/event'; +import { patternsEquals } from 'vs/base/common/glob'; import { Disposable } from 'vs/base/common/lifecycle'; import { isLinux } from 'vs/base/common/platform'; import { IDiskFileChange, ILogMessage, INonRecursiveWatchRequest, INonRecursiveWatcher } from 'vs/platform/files/common/watcher'; @@ -50,12 +50,12 @@ export class NodeJSWatcher extends Disposable implements INonRecursiveWatcher { } // Re-watch path if excludes or includes have changed - return !equals(watcher.request.excludes, request.excludes) || !equals(watcher.request.includes, request.includes); + return !patternsEquals(watcher.request.excludes, request.excludes) || !patternsEquals(watcher.request.includes, request.includes); }); // Gather paths that we should stop watching const pathsToStopWatching = Array.from(this.watchers.values()).filter(({ request }) => { - return !normalizedRequests.find(normalizedRequest => normalizedRequest.path === request.path && equals(normalizedRequest.excludes, request.excludes) && equals(normalizedRequest.includes, request.includes)); + return !normalizedRequests.find(normalizedRequest => normalizedRequest.path === request.path && patternsEquals(normalizedRequest.excludes, request.excludes) && patternsEquals(normalizedRequest.includes, request.includes)); }).map(({ request }) => request.path); // Logging diff --git a/src/vs/platform/files/node/watcher/parcel/parcelWatcher.ts b/src/vs/platform/files/node/watcher/parcel/parcelWatcher.ts index 5b0d051b30e..eab00c852b2 100644 --- a/src/vs/platform/files/node/watcher/parcel/parcelWatcher.ts +++ b/src/vs/platform/files/node/watcher/parcel/parcelWatcher.ts @@ -11,7 +11,7 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Emitter } from 'vs/base/common/event'; import { isEqualOrParent, randomPath } from 'vs/base/common/extpath'; -import { parse, ParsedPattern } from 'vs/base/common/glob'; +import { parse, ParsedPattern, patternsEquals } from 'vs/base/common/glob'; import { Disposable } from 'vs/base/common/lifecycle'; import { TernarySearchTree } from 'vs/base/common/map'; import { normalizeNFC } from 'vs/base/common/normalization'; @@ -22,7 +22,6 @@ import { realcaseSync, realpathSync } from 'vs/base/node/extpath'; import { NodeJSFileWatcherLibrary } from 'vs/platform/files/node/watcher/nodejs/nodejsWatcherLib'; import { FileChangeType } from 'vs/platform/files/common/files'; import { IDiskFileChange, ILogMessage, coalesceEvents, IRecursiveWatchRequest, IRecursiveWatcher } from 'vs/platform/files/common/watcher'; -import { equals } from 'vs/base/common/arrays'; export interface IParcelWatcherInstance { @@ -129,15 +128,15 @@ export class ParcelWatcher extends Disposable implements IRecursiveWatcher { } // Re-watch path if excludes/includes have changed or polling interval - return !equals(watcher.request.excludes, request.excludes) || !equals(watcher.request.includes, request.includes) || watcher.request.pollingInterval !== request.pollingInterval; + return !patternsEquals(watcher.request.excludes, request.excludes) || !patternsEquals(watcher.request.includes, request.includes) || watcher.request.pollingInterval !== request.pollingInterval; }); // Gather paths that we should stop watching const pathsToStopWatching = Array.from(this.watchers.values()).filter(({ request }) => { return !normalizedRequests.find(normalizedRequest => { return normalizedRequest.path === request.path && - equals(normalizedRequest.excludes, request.excludes) && - equals(normalizedRequest.includes, request.includes) && + patternsEquals(normalizedRequest.excludes, request.excludes) && + patternsEquals(normalizedRequest.includes, request.includes) && normalizedRequest.pollingInterval === request.pollingInterval; }); @@ -393,14 +392,16 @@ export class ParcelWatcher extends Disposable implements IRecursiveWatcher { return; } - // Check for excludes - const rawEvents = this.handleExcludeIncludes(parcelEvents, excludes, includes); - // Normalize events: handle NFC normalization and symlinks - const { events: normalizedEvents, rootDeleted } = this.normalizeEvents(rawEvents, watcher.request, realPathDiffers, realPathLength); + // It is important to do this before checking for includes + // and excludes to check on the original path. + const { events: normalizedEvents, rootDeleted } = this.normalizeEvents(parcelEvents, watcher.request, realPathDiffers, realPathLength); + + // Check for excludes + const includedEvents = this.handleExcludeIncludes(normalizedEvents, excludes, includes); // Coalesce events: merge events of same kind - const coalescedEvents = coalesceEvents(normalizedEvents); + const coalescedEvents = coalesceEvents(includedEvents); // Filter events: check for specific events we want to exclude const filteredEvents = this.filterEvents(coalescedEvents, watcher.request, rootDeleted); @@ -495,7 +496,7 @@ export class ParcelWatcher extends Disposable implements IRecursiveWatcher { return { realPath, realPathDiffers, realPathLength }; } - private normalizeEvents(events: IDiskFileChange[], request: IRecursiveWatchRequest, realPathDiffers: boolean, realPathLength: number): { events: IDiskFileChange[]; rootDeleted: boolean } { + private normalizeEvents(events: parcelWatcher.Event[], request: IRecursiveWatchRequest, realPathDiffers: boolean, realPathLength: number): { events: parcelWatcher.Event[]; rootDeleted: boolean } { let rootDeleted = false; for (const event of events) { @@ -519,7 +520,7 @@ export class ParcelWatcher extends Disposable implements IRecursiveWatcher { } // Check for root deleted - if (event.path === request.path && event.type === FileChangeType.DELETED) { + if (event.path === request.path && event.type === 'delete') { rootDeleted = true; } } @@ -672,7 +673,7 @@ export class ParcelWatcher extends Disposable implements IRecursiveWatcher { try { const realpath = realpathSync(request.path); if (realpath === request.path) { - this.warn(`ignoring a path for watching who's parent is already watched: ${request.path}`); + this.trace(`ignoring a path for watching who's parent is already watched: ${request.path}`); continue; // path is not a symbolic link or similar } diff --git a/src/vs/platform/files/test/node/nodejsWatcher.integrationTest.ts b/src/vs/platform/files/test/node/nodejsWatcher.integrationTest.ts index d617ce2b2e4..6bacce6495c 100644 --- a/src/vs/platform/files/test/node/nodejsWatcher.integrationTest.ts +++ b/src/vs/platform/files/test/node/nodejsWatcher.integrationTest.ts @@ -392,6 +392,12 @@ import { NodeJSWatcher } from 'vs/platform/files/node/watcher/nodejs/nodejsWatch return basicCrudTest(join(testDir, 'files-includes.txt')); }); + test('includes are supported (folder watch, relative pattern)', async function () { + await watcher.watch([{ path: testDir, excludes: [], includes: [{ base: testDir, pattern: 'files-includes.txt' }], recursive: false }]); + + return basicCrudTest(join(testDir, 'files-includes.txt')); + }); + (isWindows /* windows: cannot create file symbolic link without elevated context */ ? test.skip : test)('symlink support (folder watch)', async function () { const link = join(testDir, 'deep-linked'); const linkTarget = join(testDir, 'deep'); diff --git a/src/vs/platform/files/test/node/parcelWatcher.integrationTest.ts b/src/vs/platform/files/test/node/parcelWatcher.integrationTest.ts index 5529a525e0c..0dcb643870c 100644 --- a/src/vs/platform/files/test/node/parcelWatcher.integrationTest.ts +++ b/src/vs/platform/files/test/node/parcelWatcher.integrationTest.ts @@ -450,6 +450,12 @@ import { ltrim } from 'vs/base/common/strings'; return basicCrudTest(join(testDir, 'deep', 'newFile.txt')); }); + test('includes are supported (relative pattern)', async function () { + await watcher.watch([{ path: testDir, excludes: [], includes: [{ base: testDir, pattern: 'deep/newFile.txt' }], recursive: true }]); + + return basicCrudTest(join(testDir, 'deep', 'newFile.txt')); + }); + (isWindows /* windows: cannot create file symbolic link without elevated context */ ? test.skip : test)('symlink support (root)', async function () { const link = join(testDir, 'deep-linked'); const linkTarget = join(testDir, 'deep'); diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index d6bfb2da22e..5dbfdc1a16a 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import 'vs/css!./media/editordroptarget'; import { DataTransfers } from 'vs/base/browser/dnd'; import { addDisposableListener, DragAndDropObserver, EventHelper, EventType, isAncestor } from 'vs/base/browser/dom'; import { renderFormattedText } from 'vs/base/browser/formattedTextRenderer'; @@ -10,7 +11,6 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { toDisposable } from 'vs/base/common/lifecycle'; import { isMacintosh, isWeb } from 'vs/base/common/platform'; import { assertAllDefined, assertIsDefined } from 'vs/base/common/types'; -import 'vs/css!./media/editordroptarget'; import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -47,7 +47,9 @@ class DropOverlay extends Themable { private dropIntoPromptElement?: HTMLSpanElement; private currentDropOperation: IDropOperation | undefined; + private _disposed: boolean | undefined; + get disposed(): boolean { return !!this._disposed; } private cleanupOverlayScheduler: RunOnceScheduler; @@ -77,10 +79,6 @@ class DropOverlay extends Themable { this.create(); } - get disposed(): boolean { - return !!this._disposed; - } - private create(): void { const overlayOffsetHeight = this.getOverlayOffsetHeight(); diff --git a/src/vs/workbench/services/search/common/searchExtTypes.ts b/src/vs/workbench/services/search/common/searchExtTypes.ts index 27f646183f4..5b8449ffd83 100644 --- a/src/vs/workbench/services/search/common/searchExtTypes.ts +++ b/src/vs/workbench/services/search/common/searchExtTypes.ts @@ -74,7 +74,7 @@ export interface RelativePattern { * (like `** /*.{ts,js}` without space before / or `*.{ts,js}`) or a [relative pattern](#RelativePattern). * * Glob patterns can have the following syntax: - * * `*` to match one or more characters in a path segment + * * `*` to match zero or more characters in a path segment * * `?` to match on one character in a path segment * * `**` to match any number of path segments, including none * * `{}` to group conditions (e.g. `** /*.{ts,js}` without space before / matches all TypeScript and JavaScript files) diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index 57af873b92d..0dbe8cb9fc0 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -2032,7 +2032,7 @@ declare module 'vscode' { * (like `**​/*.{ts,js}` or `*.{ts,js}`) or a {@link RelativePattern relative pattern}. * * Glob patterns can have the following syntax: - * * `*` to match one or more characters in a path segment + * * `*` to match zero or more characters in a path segment * * `?` to match on one character in a path segment * * `**` to match any number of path segments, including none * * `{}` to group conditions (e.g. `**​/*.{ts,js}` matches all TypeScript and JavaScript files) From ad5ef7ea85b8a622f73b96394507d98809e68861 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Mon, 18 Apr 2022 09:08:08 -0400 Subject: [PATCH 118/245] Remove tab rename todo --- src/vscode-dts/vscode.proposed.tabs.d.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/vscode-dts/vscode.proposed.tabs.d.ts b/src/vscode-dts/vscode.proposed.tabs.d.ts index 1029de92ac1..ea5eafcf426 100644 --- a/src/vscode-dts/vscode.proposed.tabs.d.ts +++ b/src/vscode-dts/vscode.proposed.tabs.d.ts @@ -7,9 +7,6 @@ declare module 'vscode' { // https://github.com/Microsoft/vscode/issues/15178 - // TODO@API name alternatives for TabKind: TabInput, TabOptions, - - /** * The tab represents a single text based resource */ From 07e2a4e3f2908a5cad417d57e6ce9b3831d6955e Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 18 Apr 2022 15:02:49 -0700 Subject: [PATCH 119/245] Fix exec order indicator position when statusbar hidden. Fix #147333 --- .../contrib/notebook/browser/view/cellParts/cellExecution.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution.ts index eb6e260ec10..469e7cbef3d 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution.ts @@ -63,7 +63,8 @@ export class CellExecutionPart extends CellPart { DOM.hide(this._executionOrderLabel); } else { DOM.show(this._executionOrderLabel); - this._executionOrderLabel.style.top = `${element.layoutInfo.editorHeight}px`; + const top = element.layoutInfo.editorHeight - 22 + element.layoutInfo.statusBarHeight; + this._executionOrderLabel.style.top = `${top}px`; } } } From 3b57fde7a85d12e4583c492c29f2dcae31434ed1 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Mon, 18 Apr 2022 15:15:01 -0700 Subject: [PATCH 120/245] Fix #147652 --- .../contrib/markdown/browser/markdownDocumentRenderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts b/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts index c47ff0b58ff..68185694831 100644 --- a/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts +++ b/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts @@ -121,7 +121,7 @@ code > div { } .vscode-high-contrast code > div { - background-color: rgb(0, 0, 0); + background-color: var(--vscode-textCodeBlock-background); } .vscode-high-contrast h1 { From e7c6ad74aff28965d0189ca1dd584f5e9829cf06 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Mon, 18 Apr 2022 19:16:54 -0700 Subject: [PATCH 121/245] get zsh startup files to run in the correct order (#147434) --- .../terminal/node/terminalEnvironment.ts | 8 ++++++++ .../test/node/terminalEnvironment.test.ts | 18 +++++++++++++----- .../browser/media/shellIntegration-env.zsh | 8 ++++++++ .../browser/media/shellIntegration-profile.zsh | 8 ++++++++ .../browser/media/shellIntegration.zsh | 9 ++------- 5 files changed, 39 insertions(+), 12 deletions(-) create mode 100644 src/vs/workbench/contrib/terminal/browser/media/shellIntegration-env.zsh create mode 100644 src/vs/workbench/contrib/terminal/browser/media/shellIntegration-profile.zsh diff --git a/src/vs/platform/terminal/node/terminalEnvironment.ts b/src/vs/platform/terminal/node/terminalEnvironment.ts index dcbff469770..78009d3c17e 100644 --- a/src/vs/platform/terminal/node/terminalEnvironment.ts +++ b/src/vs/platform/terminal/node/terminalEnvironment.ts @@ -193,6 +193,14 @@ export function getShellIntegrationInjection( source: path.join(appRoot, 'out/vs/workbench/contrib/terminal/browser/media/shellIntegration.zsh'), dest: path.join(zdotdir, '.zshrc') }); + filesToCopy.push({ + source: path.join(appRoot, 'out/vs/workbench/contrib/terminal/browser/media/shellIntegration-profile.zsh'), + dest: path.join(zdotdir, '.zprofile') + }); + filesToCopy.push({ + source: path.join(appRoot, 'out/vs/workbench/contrib/terminal/browser/media/shellIntegration-env.zsh'), + dest: path.join(zdotdir, '.zshenv') + }); if (!options.showWelcome) { envMixin['VSCODE_SHELL_HIDE_WELCOME'] = '1'; } diff --git a/src/vs/platform/terminal/test/node/terminalEnvironment.test.ts b/src/vs/platform/terminal/test/node/terminalEnvironment.test.ts index 84fb8a7e08d..df8212d7c34 100644 --- a/src/vs/platform/terminal/test/node/terminalEnvironment.test.ts +++ b/src/vs/platform/terminal/test/node/terminalEnvironment.test.ts @@ -87,14 +87,22 @@ suite('platform - terminalEnvironment', () => { suite('zsh', () => { suite('should override args', () => { const expectedDir = /.+\/vscode-zsh/; - const expectedDest = /.+\/vscode-zsh\/.zshrc/; - const expectedSource = /.+\/out\/vs\/workbench\/contrib\/terminal\/browser\/media\/shellIntegration.zsh/; + const expectedDests = [/.+\/vscode-zsh\/.zshrc/, /.+\/vscode-zsh\/.zprofile/, /.+\/vscode-zsh\/.zshenv/]; + const expectedSources = [ + /.+\/out\/vs\/workbench\/contrib\/terminal\/browser\/media\/shellIntegration.zsh/, + /.+\/out\/vs\/workbench\/contrib\/terminal\/browser\/media\/shellIntegration-profile.zsh/, + /.+\/out\/vs\/workbench\/contrib\/terminal\/browser\/media\/shellIntegration-env.zsh/ + ]; function assertIsEnabled(result: IShellIntegrationConfigInjection) { strictEqual(Object.keys(result.envMixin!).length, 1); ok(result.envMixin!['ZDOTDIR']?.match(expectedDir)); - strictEqual(result.filesToCopy?.length, 1); - ok(result.filesToCopy[0].dest.match(expectedDest)); - ok(result.filesToCopy[0].source.match(expectedSource)); + strictEqual(result.filesToCopy?.length, 3); + ok(result.filesToCopy[0].dest.match(expectedDests[0])); + ok(result.filesToCopy[1].dest.match(expectedDests[1])); + ok(result.filesToCopy[2].dest.match(expectedDests[2])); + ok(result.filesToCopy[0].source.match(expectedSources[0])); + ok(result.filesToCopy[1].source.match(expectedSources[1])); + ok(result.filesToCopy[2].source.match(expectedSources[2])); } test('when undefined, []', () => { const result1 = getShellIntegrationInjection({ executable: 'zsh', args: [] }, enabledProcessOptions); diff --git a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-env.zsh b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-env.zsh new file mode 100644 index 00000000000..26ab335881a --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-env.zsh @@ -0,0 +1,8 @@ +# --------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# --------------------------------------------------------------------------------------------- + +if [[ $options[norcs] = off && -o "login" && -f ~/.zshenv ]]; then + . ~/.zshenv +fi diff --git a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-profile.zsh b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-profile.zsh new file mode 100644 index 00000000000..734bb831e11 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-profile.zsh @@ -0,0 +1,8 @@ +# --------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# --------------------------------------------------------------------------------------------- + +if [[ $options[norcs] = off && -o "login" && -f ~/.zprofile ]]; then + . ~/.zprofile +fi diff --git a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration.zsh b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration.zsh index cfba2786a3e..2dec005daf4 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration.zsh +++ b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration.zsh @@ -12,13 +12,8 @@ builtin unset ZDOTDIR # as disable it by unsetting the variable. VSCODE_SHELL_INTEGRATION=1 -if [ -f ~/.zshenv ]; then - . ~/.zshenv -fi -if [[ -o "login" && -f ~/.zprofile ]]; then - . ~/.zprofile -fi -if [ -f ~/.zshrc ]; then + +if [[ $options[norcs] = off && -f ~/.zshrc ]]; then . ~/.zshrc fi From 06e26c7a57aa829c473857716478aecf3963d76c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Apr 2022 07:38:32 +0200 Subject: [PATCH 122/245] Do not attempt to reopen files that lead to a crash in previous session (fix #114844) --- .../platform/windows/electron-main/window.ts | 83 +++++++++++-------- test/automation/src/application.ts | 6 +- 2 files changed, 54 insertions(+), 35 deletions(-) diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index aca98a776db..cc6749e78e4 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 } from 'vs/platform/storage/electron-main/storageMainService'; +import { IGlobalStorageMainService, 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'; @@ -106,23 +106,23 @@ export class CodeWindow extends Disposable implements ICodeWindow { private _lastFocusTime = -1; get lastFocusTime(): number { return this._lastFocusTime; } - get backupPath(): string | undefined { return this.currentConfig?.backupPath; } + get backupPath(): string | undefined { return this._config?.backupPath; } - get openedWorkspace(): IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined { return this.currentConfig?.workspace; } + get openedWorkspace(): IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined { return this._config?.workspace; } - get remoteAuthority(): string | undefined { return this.currentConfig?.remoteAuthority; } + get remoteAuthority(): string | undefined { return this._config?.remoteAuthority; } - private currentConfig: INativeWindowConfiguration | undefined; - get config(): INativeWindowConfiguration | undefined { return this.currentConfig; } + private _config: INativeWindowConfiguration | undefined; + get config(): INativeWindowConfiguration | undefined { return this._config; } private hiddenTitleBarStyle: boolean | undefined; get hasHiddenTitleBarStyle(): boolean { return !!this.hiddenTitleBarStyle; } - get isExtensionDevelopmentHost(): boolean { return !!(this.currentConfig?.extensionDevelopmentPath); } + get isExtensionDevelopmentHost(): boolean { return !!(this._config?.extensionDevelopmentPath); } - get isExtensionTestHost(): boolean { return !!(this.currentConfig?.extensionTestsPath); } + get isExtensionTestHost(): boolean { return !!(this._config?.extensionTestsPath); } - get isExtensionDevelopmentTestFromCli(): boolean { return this.isExtensionDevelopmentHost && this.isExtensionTestHost && !this.currentConfig?.debugId; } + get isExtensionDevelopmentTestFromCli(): boolean { return this.isExtensionDevelopmentHost && this.isExtensionTestHost && !this._config?.debugId; } //#endregion @@ -150,6 +150,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @IFileService private readonly fileService: IFileService, @IGlobalStorageMainService private readonly globalStorageMainService: IGlobalStorageMainService, + @IStorageMainService private readonly storageMainService: IStorageMainService, @IConfigurationService private readonly configurationService: IConfigurationService, @IThemeMainService private readonly themeMainService: IThemeMainService, @IWorkspacesManagementMainService private readonly workspacesManagementMainService: IWorkspacesManagementMainService, @@ -439,7 +440,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Associate properties from the load request if provided if (this.pendingLoadConfig) { - this.currentConfig = this.pendingLoadConfig; + this._config = this.pendingLoadConfig; this.pendingLoadConfig = undefined; } @@ -452,16 +453,16 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Window (Un)Maximize this._win.on('maximize', (e: Event) => { - if (this.currentConfig) { - this.currentConfig.maximized = true; + if (this._config) { + this._config.maximized = true; } app.emit('browser-window-maximize', e, this._win); }); this._win.on('unmaximize', (e: Event) => { - if (this.currentConfig) { - this.currentConfig.maximized = false; + if (this._config) { + this._config.maximized = false; } app.emit('browser-window-unmaximize', e, this._win); @@ -496,6 +497,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { if (!this.marketplaceHeadersPromise) { this.marketplaceHeadersPromise = resolveMarketplaceHeaders(this.productService.version, this.productService, this.environmentMainService, this.configurationService, this.fileService, this.globalStorageMainService); } + return this.marketplaceHeadersPromise; } @@ -546,7 +548,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { // If we run smoke tests, we never want to show a blocking dialog if (this.environmentMainService.args['enable-smoke-test-driver']) { - this.destroyWindow(false); + this.destroyWindow(false, false); return; } @@ -575,13 +577,14 @@ export class CodeWindow extends Disposable implements ICodeWindow { detail: localize('appStalledDetail', "You can reopen or close the window or keep waiting."), noLink: true, defaultId: 0, - cancelId: 1 + cancelId: 1, + checkboxLabel: this._config?.workspace ? localize('doNotRestoreEditors', "Don't restore editors") : undefined }, this._win); // Handle choice if (result.response !== 1 /* keep waiting */) { const reopen = result.response === 0; - this.destroyWindow(reopen); + this.destroyWindow(reopen, result.checkboxChecked); } } @@ -605,18 +608,32 @@ export class CodeWindow extends Disposable implements ICodeWindow { message, detail: localize('appCrashedDetail', "We are sorry for the inconvenience. You can reopen the window to continue where you left off."), noLink: true, - defaultId: 0 + defaultId: 0, + checkboxLabel: this._config?.workspace ? localize('doNotRestoreEditors', "Don't restore editors") : undefined }, this._win); // Handle choice const reopen = result.response === 0; - this.destroyWindow(reopen); + this.destroyWindow(reopen, result.checkboxChecked); } break; } } - private destroyWindow(reopen: boolean): void { + private async destroyWindow(reopen: boolean, skipRestoreEditors: boolean): Promise { + const workspace = this._config?.workspace; + + // check to discard editor state first + if (skipRestoreEditors && workspace) { + try { + const workspaceStorage = this.storageMainService.workspaceStorage(workspace); + await workspaceStorage.init(); + workspaceStorage.delete('memento/workbench.parts.editor'); + await workspaceStorage.close(); + } catch (error) { + this.logService.error(error); + } + } // 'close' event will not be fired on destroy(), so signal crash via explicit event this._onDidDestroy.fire(); @@ -625,15 +642,15 @@ export class CodeWindow extends Disposable implements ICodeWindow { this._win?.destroy(); // ask the windows service to open a new fresh window if specified - if (reopen && this.config) { + if (reopen && this._config) { // We have to reconstruct a openable from the current workspace - let workspace: IWorkspaceToOpen | IFolderToOpen | undefined = undefined; + let uriToOpen: IWorkspaceToOpen | IFolderToOpen | undefined = undefined; let forceEmpty = undefined; - if (isSingleFolderWorkspaceIdentifier(this.openedWorkspace)) { - workspace = { folderUri: this.openedWorkspace.uri }; - } else if (isWorkspaceIdentifier(this.openedWorkspace)) { - workspace = { workspaceUri: this.openedWorkspace.configPath }; + if (isSingleFolderWorkspaceIdentifier(workspace)) { + uriToOpen = { folderUri: workspace.uri }; + } else if (isWorkspaceIdentifier(workspace)) { + uriToOpen = { workspaceUri: workspace.configPath }; } else { forceEmpty = true; } @@ -641,12 +658,12 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Delegate to windows service const [window] = this.windowsMainService.open({ context: OpenContext.API, - userEnv: this.config.userEnv, + userEnv: this._config.userEnv, cli: { ...this.environmentMainService.args, _: [] // we pass in the workspace to open explicitly via `urisToOpen` }, - urisToOpen: workspace ? [workspace] : undefined, + urisToOpen: uriToOpen ? [uriToOpen] : undefined, forceEmpty, forceNewWindow: true, remoteAuthority: this.remoteAuthority @@ -659,8 +676,8 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Make sure to update our workspace config if we detect that it // was deleted - if (this.openedWorkspace?.id === workspace.id && this.currentConfig) { - this.currentConfig.workspace = undefined; + if (this._config?.workspace?.id === workspace.id && this._config) { + this._config.workspace = undefined; } } @@ -726,7 +743,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { // If this is the first time the window is loaded, we associate the paths // directly with the window because we assume the loading will just work if (this.readyState === ReadyState.NONE) { - this.currentConfig = configuration; + this._config = configuration; } // Otherwise, the window is currently showing a folder and if there is an @@ -777,7 +794,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Also, preserve the environment if we're loading from an // extension development host that had its environment set // (for https://github.com/microsoft/vscode/issues/123508) - const currentUserEnv = (this.currentConfig ?? this.pendingLoadConfig)?.userEnv; + const currentUserEnv = (this._config ?? this.pendingLoadConfig)?.userEnv; if (currentUserEnv) { const shouldPreserveLaunchCliEnvironment = isLaunchedFromCli(currentUserEnv) && !isLaunchedFromCli(configuration.userEnv); const shouldPreserveDebugEnvironmnet = this.isExtensionDevelopmentHost; @@ -817,7 +834,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { async reload(cli?: NativeParsedArgs): Promise { // Copy our current config for reuse - const configuration = Object.assign({}, this.currentConfig); + const configuration = Object.assign({}, this._config); // Validate workspace configuration.workspace = await this.validateWorkspaceBeforeReload(configuration); diff --git a/test/automation/src/application.ts b/test/automation/src/application.ts index 4506ed8e14e..675915bd26c 100644 --- a/test/automation/src/application.ts +++ b/test/automation/src/application.ts @@ -67,8 +67,10 @@ export class Application { } async restart(options?: { workspaceOrFolder?: string; extraArgs?: string[] }): Promise { - await this.stop(); - await this._start(options?.workspaceOrFolder, options?.extraArgs); + await measureAndLog((async () => { + await this.stop(); + await this._start(options?.workspaceOrFolder, options?.extraArgs); + })(), 'Application#restart()', this.logger); } private async _start(workspaceOrFolder = this.workspacePathOrFolder, extraArgs: string[] = []): Promise { From 24273bd759ec31e0ab469cdcd617c1e8921a789a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Apr 2022 07:53:30 +0200 Subject: [PATCH 123/245] storage - let main process deal with closing workspace storage --- src/vs/platform/storage/common/storageIpc.ts | 12 ++-- .../storage/electron-main/storageIpc.ts | 12 ---- .../storage/electron-main/storageMain.ts | 70 +++++++++++-------- .../electron-main/storageMainService.ts | 4 +- src/vs/workbench/browser/window.ts | 4 +- src/vs/workbench/browser/workbench.ts | 6 +- .../electron-sandbox/desktop.contribution.ts | 6 +- .../electron-sandbox/desktop.main.ts | 8 +-- src/vs/workbench/electron-sandbox/window.ts | 8 +-- 9 files changed, 66 insertions(+), 64 deletions(-) diff --git a/src/vs/platform/storage/common/storageIpc.ts b/src/vs/platform/storage/common/storageIpc.ts index e6916d0c230..5774cb82085 100644 --- a/src/vs/platform/storage/common/storageIpc.ts +++ b/src/vs/platform/storage/common/storageIpc.ts @@ -86,8 +86,9 @@ class GlobalStorageDatabaseClient extends BaseStorageDatabaseClient implements I async close(): Promise { // The global storage database is shared across all instances so - // we do not await it. However we dispose the listener for external - // changes because we no longer interested int it. + // 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(); } } @@ -101,9 +102,12 @@ class WorkspaceStorageDatabaseClient extends BaseStorageDatabaseClient implement } async close(): Promise { - const serializableRequest: ISerializableUpdateRequest = { workspace: this.workspace }; - return this.channel.call('close', serializableRequest); + // The workspace storage database is only used in this instance + // but we do not need to close it from here, the main process + // can take care of that. + + this.dispose(); } } diff --git a/src/vs/platform/storage/electron-main/storageIpc.ts b/src/vs/platform/storage/electron-main/storageIpc.ts index 87c392c46fd..9802b767d91 100644 --- a/src/vs/platform/storage/electron-main/storageIpc.ts +++ b/src/vs/platform/storage/electron-main/storageIpc.ts @@ -105,18 +105,6 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel break; } - case 'close': { - - // We only allow to close workspace scoped storage because - // global storage is shared across all windows and closes - // only on shutdown. - if (workspace) { - return storage.close(); - } - - break; - } - default: throw new Error(`Call not found: ${command}`); } diff --git a/src/vs/platform/storage/electron-main/storageMain.ts b/src/vs/platform/storage/electron-main/storageMain.ts index fbc5744c7df..384deb88135 100644 --- a/src/vs/platform/storage/electron-main/storageMain.ts +++ b/src/vs/platform/storage/electron-main/storageMain.ts @@ -212,43 +212,51 @@ abstract class BaseStorageMain extends Disposable implements IStorageMain { // a chance that the underlying DB is large // either on disk or in general. In that case // log some additional info to further diagnose - if (watch.elapsed() > BaseStorageMain.LOG_SLOW_CLOSE_THRESHOLD && this.path) { - try { - const largestEntries = top(Array.from(this._storage.items.entries()) - .map(([key, value]) => ({ key, length: value.length })), (entryA, entryB) => entryB.length - entryA.length, 5) - .map(entry => `${entry.key}:${entry.length}`).join(', '); - const dbSize = (await this.fileService.stat(URI.file(this.path))).size; - - this.logService.warn(`[storage main] detected slow close() operation: Time: ${watch.elapsed()}ms, DB size: ${dbSize}b, Large Keys: ${largestEntries}`); - - type StorageSlowCloseClassification = { - duration: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'The time it took to close the DB in ms.' }; - size: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'The size of the DB in bytes.' }; - largestEntries: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'The 5 largest keys in the DB.' }; - owner: 'bpasero'; - comment: 'Used to gain insight into reasons a database may be slow. This is used to assist with further optimizations'; - }; - - type StorageSlowCloseEvent = { - duration: number; - size: number; - largestEntries: string; - }; - - this.telemetryService.publicLog2('storageSlowClose', { - duration: watch.elapsed(), - size: dbSize, - largestEntries - }); - } catch (error) { - this.logService.error('[storage main] figuring out stats for slow DB on close() resulted in an error', error); - } + if (watch.elapsed() > BaseStorageMain.LOG_SLOW_CLOSE_THRESHOLD) { + await this.logSlowClose(watch); } // Signal as event this._onDidCloseStorage.fire(); } + private async logSlowClose(watch: StopWatch) { + if (!this.path) { + return; + } + + try { + const largestEntries = top(Array.from(this._storage.items.entries()) + .map(([key, value]) => ({ key, length: value.length })), (entryA, entryB) => entryB.length - entryA.length, 5) + .map(entry => `${entry.key}:${entry.length}`).join(', '); + const dbSize = (await this.fileService.stat(URI.file(this.path))).size; + + this.logService.warn(`[storage main] detected slow close() operation: Time: ${watch.elapsed()}ms, DB size: ${dbSize}b, Large Keys: ${largestEntries}`); + + type StorageSlowCloseClassification = { + duration: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'The time it took to close the DB in ms.' }; + size: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'The size of the DB in bytes.' }; + largestEntries: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'The 5 largest keys in the DB.' }; + owner: 'bpasero'; + comment: 'Used to gain insight into reasons a database may be slow. This is used to assist with further optimizations'; + }; + + type StorageSlowCloseEvent = { + duration: number; + size: number; + largestEntries: string; + }; + + this.telemetryService.publicLog2('storageSlowClose', { + duration: watch.elapsed(), + size: dbSize, + largestEntries + }); + } catch (error) { + this.logService.error('[storage main] figuring out stats for slow DB on close() resulted in an error', error); + } + } + private async doClose(): Promise { // Ensure we are not accidentally leaving diff --git a/src/vs/platform/storage/electron-main/storageMainService.ts b/src/vs/platform/storage/electron-main/storageMainService.ts index 6781158c321..aee6d47d758 100644 --- a/src/vs/platform/storage/electron-main/storageMainService.ts +++ b/src/vs/platform/storage/electron-main/storageMainService.ts @@ -76,7 +76,7 @@ export class StorageMainService extends Disposable implements IStorageMainServic })(); // Workspace Storage: Warmup when related window with workspace loads - this._register(this.lifecycleMainService.onWillLoadWindow(async e => { + this._register(this.lifecycleMainService.onWillLoadWindow(e => { if (e.workspace) { this.workspaceStorage(e.workspace).init(); } @@ -142,9 +142,11 @@ export class StorageMainService extends Disposable implements IStorageMainServic private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier): 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, this.telemetryService); } diff --git a/src/vs/workbench/browser/window.ts b/src/vs/workbench/browser/window.ts index 1edef45d307..229027ab56e 100644 --- a/src/vs/workbench/browser/window.ts +++ b/src/vs/workbench/browser/window.ts @@ -65,9 +65,9 @@ export class BrowserWindow extends Disposable { this._register(addDisposableListener(this.layoutService.container, EventType.DROP, e => EventHelper.stop(e, true))); // Fullscreen (Browser) - [EventType.FULLSCREEN_CHANGE, EventType.WK_FULLSCREEN_CHANGE].forEach(event => { + for (const event of [EventType.FULLSCREEN_CHANGE, EventType.WK_FULLSCREEN_CHANGE]) { this._register(addDisposableListener(document, event, () => setFullscreen(!!detectFullscreen()))); - }); + } // Fullscreen (Native) this._register(addDisposableThrottledListener(viewport, EventType.RESIZE, () => { diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index f2c5bf51ade..27c50770822 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -336,7 +336,7 @@ export class Workbench extends Layout { this.restoreFontInfo(storageService, configurationService); // Create Parts - [ + for (const { id, role, classes, options } of [ { id: Parts.TITLEBAR_PART, role: 'contentinfo', classes: ['titlebar'] }, { id: Parts.BANNER_PART, role: 'banner', classes: ['banner'] }, { id: Parts.ACTIVITYBAR_PART, role: 'none', classes: ['activitybar', this.getSideBarPosition() === Position.LEFT ? 'left' : 'right'] }, // Use role 'none' for some parts to make screen readers less chatty #114892 @@ -345,11 +345,11 @@ export class Workbench extends Layout { { id: Parts.PANEL_PART, role: 'none', classes: ['panel', 'basepanel', positionToString(this.getPanelPosition())] }, { id: Parts.AUXILIARYBAR_PART, role: 'none', classes: ['auxiliarybar', 'basepanel', this.getSideBarPosition() === Position.LEFT ? 'right' : 'left'] }, { id: Parts.STATUSBAR_PART, role: 'status', classes: ['statusbar'] } - ].forEach(({ id, role, classes, options }) => { + ]) { const partContainer = this.createPart(id, role, classes); this.getPart(id).create(partContainer, options); - }); + } // Notification Handlers this.createNotificationsHandlers(instantiationService, notificationService); diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index 805496f0f2b..8c88a219e24 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -84,21 +84,21 @@ import { ModifierKeyEmitter } from 'vs/base/browser/dom'; // Actions: macOS Native Tabs if (isMacintosh) { - [ + for (const command of [ { handler: NewWindowTabHandler, id: 'workbench.action.newWindowTab', title: { value: localize('newTab', "New Window Tab"), original: 'New Window Tab' } }, { handler: ShowPreviousWindowTabHandler, id: 'workbench.action.showPreviousWindowTab', title: { value: localize('showPreviousTab', "Show Previous Window Tab"), original: 'Show Previous Window Tab' } }, { handler: ShowNextWindowTabHandler, id: 'workbench.action.showNextWindowTab', title: { value: localize('showNextWindowTab', "Show Next Window Tab"), original: 'Show Next Window Tab' } }, { handler: MoveWindowTabToNewWindowHandler, id: 'workbench.action.moveWindowTabToNewWindow', title: { value: localize('moveWindowTabToNewWindow', "Move Window Tab to New Window"), original: 'Move Window Tab to New Window' } }, { handler: MergeWindowTabsHandlerHandler, id: 'workbench.action.mergeAllWindowTabs', title: { value: localize('mergeAllWindowTabs', "Merge All Windows"), original: 'Merge All Windows' } }, { handler: ToggleWindowTabsBarHandler, id: 'workbench.action.toggleWindowTabsBar', title: { value: localize('toggleWindowTabsBar', "Toggle Window Tabs Bar"), original: 'Toggle Window Tabs Bar' } } - ].forEach(command => { + ]) { CommandsRegistry.registerCommand(command.id, command.handler); MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command, when: ContextKeyExpr.equals('config.window.nativeTabs', true) }); - }); + } } // Actions: Developer diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts index 1df19cda720..cfaf8a03ac2 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -84,15 +84,15 @@ export class DesktopMain extends Disposable { // Files const filesToWait = this.configuration.filesToWait; const filesToWaitPaths = filesToWait?.paths; - [filesToWaitPaths, this.configuration.filesToOpenOrCreate, this.configuration.filesToDiff].forEach(paths => { + for (const paths of [filesToWaitPaths, this.configuration.filesToOpenOrCreate, this.configuration.filesToDiff]) { if (Array.isArray(paths)) { - paths.forEach(path => { + for (const path of paths) { if (path.fileUri) { path.fileUri = URI.revive(path.fileUri); } - }); + } } - }); + } if (filesToWait) { filesToWait.waitMarkerFileUri = URI.revive(filesToWait.waitMarkerFileUri); diff --git a/src/vs/workbench/electron-sandbox/window.ts b/src/vs/workbench/electron-sandbox/window.ts index 82a3ef0a045..d31a27b3881 100644 --- a/src/vs/workbench/electron-sandbox/window.ts +++ b/src/vs/workbench/electron-sandbox/window.ts @@ -132,11 +132,11 @@ export class NativeWindow extends Disposable { this._register(this.editorService.onDidActiveEditorChange(() => this.updateTouchbarMenu())); // prevent opening a real URL inside the window - [EventType.DRAG_OVER, EventType.DROP].forEach(event => { + for (const event of [EventType.DRAG_OVER, EventType.DROP]) { window.document.body.addEventListener(event, (e: DragEvent) => { EventHelper.stop(e); }); - }); + } // Support runAction event ipcRenderer.on('vscode:runAction', async (event: unknown, request: INativeRunActionInWindowRequest) => { @@ -812,9 +812,9 @@ export class NativeWindow extends Disposable { private doAddFolders(): void { const foldersToAdd: IWorkspaceFolderCreationData[] = []; - this.pendingFoldersToAdd.forEach(folder => { + for (const folder of this.pendingFoldersToAdd) { foldersToAdd.push(({ uri: folder })); - }); + } this.pendingFoldersToAdd = []; From a9010d094a33ed219fda67a65bc6ca636e7f3920 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Apr 2022 09:03:17 +0200 Subject: [PATCH 124/245] smoke - support `retryWithRestart` in web too --- test/automation/src/playwrightBrowser.ts | 5 ++--- test/smoke/src/main.ts | 2 +- test/smoke/src/utils.ts | 8 +++++++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/test/automation/src/playwrightBrowser.ts b/test/automation/src/playwrightBrowser.ts index 5d85b0e910a..f4e28668ab0 100644 --- a/test/automation/src/playwrightBrowser.ts +++ b/test/automation/src/playwrightBrowser.ts @@ -6,8 +6,7 @@ import * as playwright from '@playwright/test'; import { ChildProcess, spawn } from 'child_process'; import { join } from 'path'; -import { mkdir } from 'fs'; -import { promisify } from 'util'; +import * as mkdirp from 'mkdirp'; import { URI } from 'vscode-uri'; import { Logger, measureAndLog } from './logger'; import type { LaunchOptions } from './code'; @@ -35,7 +34,7 @@ async function launchServer(options: LaunchOptions) { const { userDataDir, codePath, extensionsPath, logger, logsPath } = options; const codeServerPath = codePath ?? process.env.VSCODE_REMOTE_SERVER_PATH; const agentFolder = userDataDir; - await measureAndLog(promisify(mkdir)(agentFolder), `mkdir(${agentFolder})`, logger); + await measureAndLog(mkdirp(agentFolder), `mkdirp(${agentFolder})`, logger); const env = { VSCODE_REMOTE_SERVER_PATH: codeServerPath, diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 7f5accccec7..438ea351ff0 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -107,7 +107,7 @@ const testDataPath = path.join(os.tmpdir(), 'vscsmoke'); if (fs.existsSync(testDataPath)) { rimraf.sync(testDataPath); } -fs.mkdirSync(testDataPath); +mkdirp.sync(testDataPath); process.once('exit', () => { try { rimraf.sync(testDataPath); diff --git a/test/smoke/src/utils.ts b/test/smoke/src/utils.ts index bfcbd5f9e18..049f20535d5 100644 --- a/test/smoke/src/utils.ts +++ b/test/smoke/src/utils.ts @@ -150,9 +150,13 @@ export function timeout(i: number) { } export async function retryWithRestart(app: Application, testFn: () => Promise, retries = 3, timeoutMs = 20000): Promise { + let lastError: Error | undefined = undefined; for (let i = 0; i < retries; i++) { const result = await Promise.race([ - testFn().then(() => true, error => { throw error; }), + testFn().then(() => true, error => { + lastError = error; + return false; + }), timeout(timeoutMs).then(() => false) ]); @@ -162,6 +166,8 @@ export async function retryWithRestart(app: Application, testFn: () => Promise { From fc8a77abff7ccd7d5e70b7d1a4137e325404c0cd Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 19 Apr 2022 09:34:34 +0200 Subject: [PATCH 125/245] some jsdoc --- src/vscode-dts/vscode.proposed.tabs.d.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/vscode-dts/vscode.proposed.tabs.d.ts b/src/vscode-dts/vscode.proposed.tabs.d.ts index ea5eafcf426..6d1f6e9b00a 100644 --- a/src/vscode-dts/vscode.proposed.tabs.d.ts +++ b/src/vscode-dts/vscode.proposed.tabs.d.ts @@ -87,6 +87,9 @@ declare module 'vscode' { * The uri of the modified notebook. */ readonly modified: Uri; + /** + * The type of notebook. Maps to {@linkcode NotebookDocument.notebookType NotebookDocuments's notebookType} + */ readonly notebookType: string; constructor(original: Uri, modified: Uri, notebookType: string); } @@ -150,6 +153,9 @@ declare module 'vscode' { export const tabGroups: TabGroups; } + /** + * An event describing change to tabs. + */ export interface TabChangeEvent { /** * The tabs that have been opened @@ -186,7 +192,7 @@ declare module 'vscode' { } /** - * Represents a group of tabs. A tab group itself consists of multiple tab + * Represents a group of tabs. A tab group itself consists of multiple tabs. */ export interface TabGroup { /** @@ -219,6 +225,9 @@ declare module 'vscode' { readonly tabs: readonly Tab[]; } + /** + * Represents the main editor area which consists of multple groups which contain tabs. + */ export interface TabGroups { /** * All the groups within the group container From ec71e5c3e2c1070f104e322948d4b5031260b3bd Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Apr 2022 10:31:03 +0200 Subject: [PATCH 126/245] more sandboxed usages in ext host starter (#147666) --- src/vs/base/common/platform.ts | 2 -- src/vs/base/common/processes.ts | 31 ++++++++++++++++++- src/vs/base/node/processes.ts | 29 ----------------- src/vs/base/parts/ipc/node/ipc.cp.ts | 3 +- .../parts/sandbox/electron-browser/preload.js | 23 +++----------- .../parts/sandbox/electron-sandbox/globals.ts | 5 +++ src/vs/platform/native/common/native.ts | 1 + .../electron-main/nativeHostMainService.ts | 5 +++ .../sharedProcessWorkerMain.ts | 2 +- src/vs/server/node/extensionHostConnection.ts | 2 +- .../localProcessExtensionHost.ts | 18 ++++------- .../electron-browser/workbenchTestServices.ts | 1 + 12 files changed, 56 insertions(+), 66 deletions(-) diff --git a/src/vs/base/common/platform.ts b/src/vs/base/common/platform.ts index b2e6fef5f92..e0ea1d62c64 100644 --- a/src/vs/base/common/platform.ts +++ b/src/vs/base/common/platform.ts @@ -43,7 +43,6 @@ export interface INodeProcess { versions?: { electron?: string; }; - sandboxed?: boolean; type?: string; cwd: () => string; } @@ -65,7 +64,6 @@ if (typeof globals.vscode !== 'undefined' && typeof globals.vscode.process !== ' const isElectronProcess = typeof nodeProcess?.versions?.electron === 'string'; const isElectronRenderer = isElectronProcess && nodeProcess?.type === 'renderer'; -export const isElectronSandboxed = isElectronRenderer && nodeProcess?.sandboxed; interface INavigator { userAgent: string; diff --git a/src/vs/base/common/processes.ts b/src/vs/base/common/processes.ts index db23e105808..3e5c8b7e134 100644 --- a/src/vs/base/common/processes.ts +++ b/src/vs/base/common/processes.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IProcessEnvironment } from 'vs/base/common/platform'; +import { IProcessEnvironment, isLinux, isMacintosh } from 'vs/base/common/platform'; /** * Options to be passed to the external program or shell. @@ -124,3 +124,32 @@ export function sanitizeProcessEnvironment(env: IProcessEnvironment, ...preserve } }); } + +/** + * Remove dangerous environment variables that have caused crashes + * in forked processes (i.e. in ELECTRON_RUN_AS_NODE processes) + * + * @param env The env object to change + */ +export function removeDangerousEnvVariables(env: IProcessEnvironment | undefined): void { + if (!env) { + return; + } + + // Unset `DEBUG`, as an invalid value might lead to process crashes + // See https://github.com/microsoft/vscode/issues/130072 + delete env['DEBUG']; + + if (isMacintosh) { + // Unset `DYLD_LIBRARY_PATH`, as it leads to process crashes + // See https://github.com/microsoft/vscode/issues/104525 + // See https://github.com/microsoft/vscode/issues/105848 + delete env['DYLD_LIBRARY_PATH']; + } + + if (isLinux) { + // Unset `LD_PRELOAD`, as it might lead to process crashes + // See https://github.com/microsoft/vscode/issues/134177 + delete env['LD_PRELOAD']; + } +} diff --git a/src/vs/base/node/processes.ts b/src/vs/base/node/processes.ts index 4085e82397c..c751a692473 100644 --- a/src/vs/base/node/processes.ts +++ b/src/vs/base/node/processes.ts @@ -87,35 +87,6 @@ function terminateProcess(process: cp.ChildProcess, cwd?: string): Promise; + findFreePort(startPort: number, giveUpAfter: number, timeout: number, stride?: number): Promise; // Registry (windows only) windowsGetStringRegKey(hive: 'HKEY_CURRENT_USER' | 'HKEY_LOCAL_MACHINE' | 'HKEY_CLASSES_ROOT' | 'HKEY_USERS' | 'HKEY_CURRENT_CONFIG', path: string, name: string): Promise; diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index ac91aa14727..0233e52e050 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -19,6 +19,7 @@ import { URI } from 'vs/base/common/uri'; import { realpath } from 'vs/base/node/extpath'; import { virtualMachineHint } from 'vs/base/node/id'; import { Promises, SymlinkSupport } from 'vs/base/node/pfs'; +import { findFreePort } from 'vs/base/node/ports'; import { MouseInputEvent } from 'vs/base/parts/sandbox/common/electronTypes'; import { localize } from 'vs/nls'; import { ISerializableCommandAction } from 'vs/platform/action/common/action'; @@ -736,6 +737,10 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } } + findFreePort(windowId: number | undefined, startPort: number, giveUpAfter: number, timeout: number, stride = 1): Promise { + return findFreePort(startPort, giveUpAfter, timeout, stride); + } + //#endregion diff --git a/src/vs/platform/sharedProcess/electron-browser/sharedProcessWorkerMain.ts b/src/vs/platform/sharedProcess/electron-browser/sharedProcessWorkerMain.ts index f0af8c6648e..165268c5e12 100644 --- a/src/vs/platform/sharedProcess/electron-browser/sharedProcessWorkerMain.ts +++ b/src/vs/platform/sharedProcess/electron-browser/sharedProcessWorkerMain.ts @@ -12,7 +12,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { deepClone } from 'vs/base/common/objects'; import { withNullAsUndefined } from 'vs/base/common/types'; -import { removeDangerousEnvVariables } from 'vs/base/node/processes'; +import { removeDangerousEnvVariables } from 'vs/base/common/processes'; import { hash, ISharedProcessWorkerConfiguration, ISharedProcessWorkerProcessExit } from 'vs/platform/sharedProcess/common/sharedProcessWorkerService'; import { SharedProcessWorkerMessages, ISharedProcessToWorkerMessage, ISharedProcessWorkerEnvironment, IWorkerToSharedProcessMessage } from 'vs/platform/sharedProcess/electron-browser/sharedProcessWorker'; diff --git a/src/vs/server/node/extensionHostConnection.ts b/src/vs/server/node/extensionHostConnection.ts index 4915eb0c283..96a972433da 100644 --- a/src/vs/server/node/extensionHostConnection.ts +++ b/src/vs/server/node/extensionHostConnection.ts @@ -19,7 +19,7 @@ import { IExtHostReadyMessage, IExtHostSocketMessage, IExtHostReduceGraceTimeMes import { IServerEnvironmentService } from 'vs/server/node/serverEnvironmentService'; import { IProcessEnvironment, isWindows } from 'vs/base/common/platform'; import { logRemoteEntry } from 'vs/workbench/services/extensions/common/remoteConsoleUtil'; -import { removeDangerousEnvVariables } from 'vs/base/node/processes'; +import { removeDangerousEnvVariables } from 'vs/base/common/processes'; import { IExtensionHostStatusService } from 'vs/server/node/extensionHostStatusService'; export async function buildUserEnvironment(startParamsEnv: { [key: string]: string | null } = {}, withUserShellEnvironment: boolean, language: string, isDebug: boolean, environmentService: IServerEnvironmentService, logService: ILogService): Promise { diff --git a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts index 31e98e06a85..46a45aa5cf5 100644 --- a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts @@ -4,14 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { Server, Socket, createServer } from 'net'; -import { findFreePort } from 'vs/base/node/ports'; import { createRandomIPCHandle, NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; import * as nls from 'vs/nls'; import { timeout } from 'vs/base/common/async'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Emitter, Event } from 'vs/base/common/event'; -import { toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import * as objects from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; @@ -42,9 +41,10 @@ import { IOutputChannelRegistry, Extensions } from 'vs/workbench/services/output import { IShellEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/shellEnvironmentService'; import { IExtensionHostProcessOptions, IExtensionHostStarter } from 'vs/platform/extensions/common/extensionHostStarter'; import { SerializedError } from 'vs/base/common/errors'; -import { removeDangerousEnvVariables } from 'vs/base/node/processes'; +import { removeDangerousEnvVariables } from 'vs/base/common/processes'; import { StopWatch } from 'vs/base/common/stopwatch'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; +import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; export interface ILocalProcessExtensionHostInitData { readonly autoStart: boolean; @@ -171,7 +171,7 @@ export class LocalProcessExtensionHost implements IExtensionHost { this._toDispose.add(this._onExit); this._toDispose.add(this._lifecycleService.onWillShutdown(e => this._onWillShutdown(e))); - this._toDispose.add(this._lifecycleService.onDidShutdown(reason => this.terminate())); + this._toDispose.add(this._lifecycleService.onDidShutdown(() => this.terminate())); this._toDispose.add(this._extensionHostDebugService.onClose(event => { if (this._isExtensionDevHost && this._environmentService.debugExtensionHost.debugId === event.sessionId) { this._nativeHostService.closeWindow(); @@ -182,12 +182,6 @@ export class LocalProcessExtensionHost implements IExtensionHost { this._hostService.reload(); } })); - - const globalExitListener = () => this.terminate(); - process.once('exit', globalExitListener); - this._toDispose.add(toDisposable(() => { - process.removeListener('exit' as 'loaded', globalExitListener); // https://github.com/electron/electron/issues/21475 - })); } public dispose(): void { @@ -380,7 +374,7 @@ export class LocalProcessExtensionHost implements IExtensionHost { } const expected = this._environmentService.debugExtensionHost.port; - const port = await findFreePort(expected, 10 /* try 10 ports */, 5000 /* try up to 5 seconds */, 2048 /* skip 2048 ports between attempts */); + const port = await this._nativeHostService.findFreePort(expected, 10 /* try 10 ports */, 5000 /* try up to 5 seconds */, 2048 /* skip 2048 ports between attempts */); if (!this._isExtensionDevTestFromCli) { if (!port) { @@ -639,7 +633,7 @@ export class LocalProcessExtensionHost implements IExtensionHost { return withNullAsUndefined(this._inspectPort); } - public terminate(): void { + private terminate(): void { if (this._terminating) { return; } diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index ba5b81b1d97..2b52b2c6c27 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -245,6 +245,7 @@ export class TestNativeHostService implements INativeHostService { async toggleDevTools(): Promise { } async toggleSharedProcessWindow(): Promise { } async resolveProxy(url: string): Promise { return undefined; } + async findFreePort(startPort: number, giveUpAfter: number, timeout: number, stride?: number): Promise { return -1; } async readClipboardText(type?: 'selection' | 'clipboard' | undefined): Promise { return ''; } async writeClipboardText(text: string, type?: 'selection' | 'clipboard' | undefined): Promise { } async readClipboardFindText(): Promise { return ''; } From c4769cfdcd522942f3d4c27cacaa13deb8905dac Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Apr 2022 11:05:40 +0200 Subject: [PATCH 127/245] Confirmation of Clear Recent projects (fix #139392) --- .../browser/parts/editor/editorActions.ts | 34 ++++++++++++++++--- .../browser/commandsQuickAccess.ts | 14 ++++++++ 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index 0995d4e6e48..a993829e847 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -19,7 +19,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; -import { IFileDialogService, ConfirmResult } from 'vs/platform/dialogs/common/dialogs'; +import { IFileDialogService, ConfirmResult, IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { ItemActivation, IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { AllEditorsByMostRecentlyUsedQuickAccess, ActiveGroupEditorsByMostRecentlyUsedQuickAccess, AllEditorsByAppearanceQuickAccess } from 'vs/workbench/browser/parts/editor/editorQuickAccess'; import { Codicon } from 'vs/base/common/codicons'; @@ -1483,13 +1483,26 @@ export class ClearRecentFilesAction extends Action { id: string, label: string, @IWorkspacesService private readonly workspacesService: IWorkspacesService, - @IHistoryService private readonly historyService: IHistoryService + @IHistoryService private readonly historyService: IHistoryService, + @IDialogService private readonly dialogService: IDialogService ) { super(id, label); } override async run(): Promise { + // Ask for confirmation + const { confirmed } = await this.dialogService.confirm({ + message: localize('confirmClearRecentsMessage', "Do you want to clear all recently opened files and workspaces?"), + detail: localize('confirmClearDetail', "This action is irreversible!"), + primaryButton: localize({ key: 'clearButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Clear"), + type: 'warning' + }); + + if (!confirmed) { + return; + } + // Clear global recently opened this.workspacesService.clearRecentlyOpened(); @@ -1746,14 +1759,27 @@ export class ClearEditorHistoryAction extends Action { constructor( id: string, label: string, - @IHistoryService private readonly historyService: IHistoryService + @IHistoryService private readonly historyService: IHistoryService, + @IDialogService private readonly dialogService: IDialogService ) { super(id, label); } override async run(): Promise { - // Editor history + // Ask for confirmation + const { confirmed } = await this.dialogService.confirm({ + message: localize('confirmClearEditorHistoryMessage', "Do you want to clear the history of recently opened editors?"), + detail: localize('confirmClearDetail', "This action is irreversible!"), + primaryButton: localize({ key: 'clearButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Clear"), + type: 'warning' + }); + + if (!confirmed) { + return; + } + + // Clear editor history this.historyService.clear(); } } diff --git a/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts b/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts index 6d2258b96f1..755c6fa307c 100644 --- a/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts +++ b/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts @@ -188,9 +188,23 @@ export class ClearCommandHistoryAction extends Action2 { async run(accessor: ServicesAccessor): Promise { const configurationService = accessor.get(IConfigurationService); const storageService = accessor.get(IStorageService); + const dialogService = accessor.get(IDialogService); const commandHistoryLength = CommandsHistory.getConfiguredCommandHistoryLength(configurationService); if (commandHistoryLength > 0) { + + // Ask for confirmation + const { confirmed } = await dialogService.confirm({ + message: localize('confirmClearMessage', "Do you want to clear the history of recently used commands?"), + detail: localize('confirmClearDetail', "This action is irreversible!"), + primaryButton: localize({ key: 'clearButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Clear"), + type: 'warning' + }); + + if (!confirmed) { + return; + } + CommandsHistory.clearHistory(configurationService, storageService); } } From 4edae49746cafba40d46070f8a6ac08f279840fa Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 19 Apr 2022 11:10:49 +0200 Subject: [PATCH 128/245] add `{` and closing default brackets as boost/separator characters, https://github.com/microsoft/vscode/issues/147423 --- src/vs/base/common/filters.ts | 5 +++++ src/vs/base/test/common/filters.test.ts | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index 0308166c708..d0a8a09aa70 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -472,8 +472,13 @@ function isSeparatorAtPos(value: string, index: number): boolean { case CharCode.Colon: case CharCode.DollarSign: case CharCode.LessThan: + case CharCode.GreaterThan: case CharCode.OpenParen: + case CharCode.CloseParen: case CharCode.OpenSquareBracket: + case CharCode.CloseSquareBracket: + case CharCode.OpenCurlyBrace: + case CharCode.CloseCurlyBrace: return true; case undefined: return false; diff --git a/src/vs/base/test/common/filters.test.ts b/src/vs/base/test/common/filters.test.ts index 9ef26186cb9..5fcd4f9f097 100644 --- a/src/vs/base/test/common/filters.test.ts +++ b/src/vs/base/test/common/filters.test.ts @@ -575,4 +575,10 @@ suite('Filters', () => { assert.ok(bScore); assert.ok(aScore[0] === bScore[0]); }); + + test('Unexpected suggest highlighting ignores whole word match in favor of matching first letter#147423', function () { + + assertMatches('i', 'machine/{id}', 'machine/{^id}', fuzzyScore); + assertMatches('ok', 'obobobf{ok}/user', '^obobobf{o^k}/user', fuzzyScore); + }); }); From d38141a51979b6cff8091c964cfc0e09683b7044 Mon Sep 17 00:00:00 2001 From: Robo Date: Tue, 19 Apr 2022 19:34:10 +0900 Subject: [PATCH 129/245] ci: build arm64 server with centos7-devtoolset8 (#147676) --- .../linux/product-build-linux-client.yml | 2 +- .../linux/product-build-linux-server.yml | 31 +++++++++++++------ .../scripts/install-remote-dependencies.sh | 11 ++++++- build/azure-pipelines/product-build.yml | 7 +++++ 4 files changed, 39 insertions(+), 12 deletions(-) diff --git a/build/azure-pipelines/linux/product-build-linux-client.yml b/build/azure-pipelines/linux/product-build-linux-client.yml index 66b9e15e881..d5be0e3b606 100644 --- a/build/azure-pipelines/linux/product-build-linux-client.yml +++ b/build/azure-pipelines/linux/product-build-linux-client.yml @@ -148,7 +148,7 @@ steps: rm -rf remote/node_modules tar -xzf $(Build.ArtifactStagingDirectory)/reh_node_modules-$(VSCODE_ARCH).tar.gz --directory $(Build.SourcesDirectory)/remote displayName: Extract server node_modules output - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) + condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'armhf')) - script: | set -e diff --git a/build/azure-pipelines/linux/product-build-linux-server.yml b/build/azure-pipelines/linux/product-build-linux-server.yml index 94145131c37..71b3936f7c9 100644 --- a/build/azure-pipelines/linux/product-build-linux-server.yml +++ b/build/azure-pipelines/linux/product-build-linux-server.yml @@ -10,6 +10,16 @@ steps: KeyVaultName: vscode SecretsFilter: "github-distro-mixin-password,ESRP-PKI,esrp-aad-username,esrp-aad-password" + - task: Docker@1 + displayName: "Pull Docker image" + inputs: + azureSubscriptionEndpoint: "vscode-builds-subscription" + azureContainerRegistry: vscodehub.azurecr.io + command: "Run an image" + imageName: "vscode-linux-build-agent:centos7-devtoolset8-arm64" + containerCommand: uname + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'arm64')) + - script: | set -e cat << EOF > ~/.netrc @@ -45,21 +55,22 @@ steps: - script: | set -e - export npm_config_arch=$(NPM_ARCH) - - for i in {1..3}; do # try 3 times, for Terrapin - yarn --cwd remote --frozen-lockfile --check-files && break - if [ $i -eq 3 ]; then - echo "Yarn failed too many times" >&2 - exit 1 - fi - echo "Yarn failed $i, trying again..." - done + $(pwd)/build/azure-pipelines/linux/scripts/install-remote-dependencies.sh displayName: Install dependencies env: GITHUB_TOKEN: "$(github-distro-mixin-password)" condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) + - script: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + displayName: Register Docker QEMU + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'arm64')) + + - script: | + set -e + docker run -e VSCODE_QUALITY -v $(pwd):/root/vscode -v ~/.netrc:/root/.netrc vscodehub.azurecr.io/vscode-linux-build-agent:centos7-devtoolset8-arm64 /root/vscode/build/azure-pipelines/linux/scripts/install-remote-dependencies.sh + displayName: Install dependencies via qemu + condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'arm64')) + - script: | set -e tar -cz --ignore-failed-read -f $(Build.ArtifactStagingDirectory)/reh_node_modules-$(VSCODE_ARCH).tar.gz -C $(Build.SourcesDirectory)/remote node_modules diff --git a/build/azure-pipelines/linux/scripts/install-remote-dependencies.sh b/build/azure-pipelines/linux/scripts/install-remote-dependencies.sh index f849b3acef2..d2f62087661 100755 --- a/build/azure-pipelines/linux/scripts/install-remote-dependencies.sh +++ b/build/azure-pipelines/linux/scripts/install-remote-dependencies.sh @@ -2,4 +2,13 @@ set -e echo "Installing remote dependencies" -(cd remote && rm -rf node_modules && yarn --verbose) +(cd remote && rm -rf node_modules) + +for i in {1..3}; do # try 3 times, for Terrapin + yarn --cwd remote --frozen-lockfile --check-files && break + if [ $i -eq 3 ]; then + echo "Yarn failed too many times" >&2 + exit 1 + fi + echo "Yarn failed $i, trying again..." +done diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index ff13d02ed8a..f642d0a6805 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -207,6 +207,13 @@ stages: steps: - template: linux/product-build-linux-server.yml + - ${{ if eq(parameters.VSCODE_BUILD_LINUX, true) }}: + - job: arm64 + variables: + VSCODE_ARCH: arm64 + steps: + - template: linux/product-build-linux-server.yml + - ${{ if and(eq(parameters.VSCODE_COMPILE_ONLY, false), eq(variables['VSCODE_BUILD_STAGE_LINUX'], true)) }}: - stage: Linux dependsOn: From 9ce2a653c872207fa91ad54cf7834547793d86ce Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 19 Apr 2022 12:56:55 +0200 Subject: [PATCH 130/245] macOS: declare folder support (#147686) * wip * add utis --- build/lib/electron.js | 15 +++++++++------ build/lib/electron.ts | 16 ++++++++++------ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/build/lib/electron.js b/build/lib/electron.js index f623e8cb1b0..362f6c38e69 100644 --- a/build/lib/electron.js +++ b/build/lib/electron.js @@ -37,7 +37,7 @@ const darwinCreditsTemplate = product.darwinCredits && _.template(fs.readFileSyn * If you call `darwinBundleDocumentType(..., 'bat', 'Windows command script')`, the file type is `"Windows command script"`, * and the `'bat'` darwin icon is used. */ -function darwinBundleDocumentType(extensions, icon, nameOrSuffix) { +function darwinBundleDocumentType(extensions, icon, nameOrSuffix, utis) { // If given a suffix, generate a name from it. If not given anything, default to 'document' if (isDocumentSuffix(nameOrSuffix) || !nameOrSuffix) { nameOrSuffix = icon.charAt(0).toUpperCase() + icon.slice(1) + ' ' + (nameOrSuffix ?? 'document'); @@ -46,8 +46,9 @@ function darwinBundleDocumentType(extensions, icon, nameOrSuffix) { name: nameOrSuffix, role: 'Editor', ostypes: ['TEXT', 'utxt', 'TUTX', '****'], - extensions: extensions, - iconFile: 'resources/darwin/' + icon + '.icns' + extensions, + iconFile: 'resources/darwin/' + icon + '.icns', + utis }; } /** @@ -65,11 +66,11 @@ function darwinBundleDocumentTypes(types, icon) { return Object.keys(types).map((name) => { const extensions = types[name]; return { - name: name, + name, role: 'Editor', ostypes: ['TEXT', 'utxt', 'TUTX', '****'], extensions: Array.isArray(extensions) ? extensions : [extensions], - iconFile: 'resources/darwin/' + icon + '.icns', + iconFile: 'resources/darwin/' + icon + '.icns' }; }); } @@ -156,7 +157,9 @@ exports.config = { darwinBundleDocumentType([ 'containerfile', 'ctp', 'dot', 'edn', 'handlebars', 'hbs', 'ml', 'mli', 'pl', 'pl6', 'pm', 'pm6', 'pod', 'pp', 'properties', 'psgi', 'rt', 't' - ], 'default', product.nameLong + ' document') + ], 'default', product.nameLong + ' document'), + // Folder support () + darwinBundleDocumentType([], 'default', 'Folder', ['public.folder']) ], darwinBundleURLTypes: [{ role: 'Viewer', diff --git a/build/lib/electron.ts b/build/lib/electron.ts index a3d696b75cc..ed91f8c2f71 100644 --- a/build/lib/electron.ts +++ b/build/lib/electron.ts @@ -19,6 +19,7 @@ type DarwinDocumentType = { ostypes: string[]; extensions: string[]; iconFile: string; + utis?: string[]; }; function isDocumentSuffix(str?: string): str is DarwinDocumentSuffix { @@ -50,7 +51,7 @@ const darwinCreditsTemplate = product.darwinCredits && _.template(fs.readFileSyn * If you call `darwinBundleDocumentType(..., 'bat', 'Windows command script')`, the file type is `"Windows command script"`, * and the `'bat'` darwin icon is used. */ -function darwinBundleDocumentType(extensions: string[], icon: string, nameOrSuffix?: string | DarwinDocumentSuffix): DarwinDocumentType { +function darwinBundleDocumentType(extensions: string[], icon: string, nameOrSuffix?: string | DarwinDocumentSuffix, utis?: string[]): DarwinDocumentType { // If given a suffix, generate a name from it. If not given anything, default to 'document' if (isDocumentSuffix(nameOrSuffix) || !nameOrSuffix) { nameOrSuffix = icon.charAt(0).toUpperCase() + icon.slice(1) + ' ' + (nameOrSuffix ?? 'document'); @@ -60,8 +61,9 @@ function darwinBundleDocumentType(extensions: string[], icon: string, nameOrSuff name: nameOrSuffix, role: 'Editor', ostypes: ['TEXT', 'utxt', 'TUTX', '****'], - extensions: extensions, - iconFile: 'resources/darwin/' + icon + '.icns' + extensions, + iconFile: 'resources/darwin/' + icon + '.icns', + utis }; } @@ -80,11 +82,11 @@ function darwinBundleDocumentTypes(types: { [name: string]: string | string[] }, return Object.keys(types).map((name: string): DarwinDocumentType => { const extensions = types[name]; return { - name: name, + name, role: 'Editor', ostypes: ['TEXT', 'utxt', 'TUTX', '****'], extensions: Array.isArray(extensions) ? extensions : [extensions], - iconFile: 'resources/darwin/' + icon + '.icns', + iconFile: 'resources/darwin/' + icon + '.icns' } as DarwinDocumentType; }); } @@ -172,7 +174,9 @@ export const config = { darwinBundleDocumentType([ 'containerfile', 'ctp', 'dot', 'edn', 'handlebars', 'hbs', 'ml', 'mli', 'pl', 'pl6', 'pm', 'pm6', 'pod', 'pp', 'properties', 'psgi', 'rt', 't' - ], 'default', product.nameLong + ' document') + ], 'default', product.nameLong + ' document'), + // Folder support () + darwinBundleDocumentType([], 'default', 'Folder', ['public.folder']) ], darwinBundleURLTypes: [{ role: 'Viewer', From cf63e1839c2d1edb6fae8b91715ccc2131436e86 Mon Sep 17 00:00:00 2001 From: deepak1556 Date: Tue, 19 Apr 2022 20:19:47 +0900 Subject: [PATCH 131/245] ci: fix 403 when downloading ripgrep prebuilt --- build/azure-pipelines/linux/product-build-linux-server.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build/azure-pipelines/linux/product-build-linux-server.yml b/build/azure-pipelines/linux/product-build-linux-server.yml index 71b3936f7c9..07fa3e46496 100644 --- a/build/azure-pipelines/linux/product-build-linux-server.yml +++ b/build/azure-pipelines/linux/product-build-linux-server.yml @@ -67,8 +67,10 @@ steps: - script: | set -e - docker run -e VSCODE_QUALITY -v $(pwd):/root/vscode -v ~/.netrc:/root/.netrc vscodehub.azurecr.io/vscode-linux-build-agent:centos7-devtoolset8-arm64 /root/vscode/build/azure-pipelines/linux/scripts/install-remote-dependencies.sh + docker run -e VSCODE_QUALITY -e GITHUB_TOKEN -v $(pwd):/root/vscode -v ~/.netrc:/root/.netrc vscodehub.azurecr.io/vscode-linux-build-agent:centos7-devtoolset8-arm64 /root/vscode/build/azure-pipelines/linux/scripts/install-remote-dependencies.sh displayName: Install dependencies via qemu + env: + GITHUB_TOKEN: "$(github-distro-mixin-password)" condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'arm64')) - script: | From bab1b25c0be5956ad60e93d573a99660dc431fb2 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 19 Apr 2022 13:45:01 +0200 Subject: [PATCH 132/245] Polish comment line numbers in view Part of #146510 --- .../contrib/comments/browser/commentsTreeViewer.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts index a65cffe56f7..78a63890aa3 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts @@ -212,7 +212,11 @@ export class CommentNodeRenderer implements IListRenderer templateData.threadMetadata.commentPreview.title = renderedComment.element.textContent ?? ''; } - templateData.threadMetadata.range.textContent = `[${node.element.range.startLineNumber}-${node.element.range.endLineNumber}]`; + if (node.element.range.startLineNumber === node.element.range.endLineNumber) { + templateData.threadMetadata.range.textContent = nls.localize('commentLine', "[Ln {0}]", node.element.range.startLineNumber); + } else { + templateData.threadMetadata.range.textContent = nls.localize('commentRange', "[Ln {0}-{1}]", node.element.range.startLineNumber, node.element.range.endLineNumber); + } if (!node.element.hasReply()) { templateData.repliesMetadata.container.style.display = 'none'; From 02a92ee13f8fa6bded3b7c85f08d572fe8ab99c0 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 19 Apr 2022 13:55:44 +0200 Subject: [PATCH 133/245] Revert "Workspace trust - add tracing to track down integration test failure (#142609)" This reverts commit 0fb0a6df31fe49ab17a67ef33192aca9e1434699. --- src/vs/workbench/browser/web.main.ts | 2 +- .../electron-sandbox/desktop.main.ts | 2 +- .../workspaces/common/workspaceTrust.ts | 53 +------------------ .../test/common/workspaceTrust.test.ts | 8 +-- 4 files changed, 6 insertions(+), 59 deletions(-) diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 5ad8d30aacc..e8ca913a532 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -253,7 +253,7 @@ export class BrowserMain extends Disposable { const workspaceTrustEnablementService = new WorkspaceTrustEnablementService(configurationService, environmentService); serviceCollection.set(IWorkspaceTrustEnablementService, workspaceTrustEnablementService); - const workspaceTrustManagementService = new WorkspaceTrustManagementService(configurationService, remoteAuthorityResolverService, storageService, uriIdentityService, environmentService, configurationService, workspaceTrustEnablementService, logService); + const workspaceTrustManagementService = new WorkspaceTrustManagementService(configurationService, remoteAuthorityResolverService, storageService, uriIdentityService, environmentService, configurationService, workspaceTrustEnablementService); serviceCollection.set(IWorkspaceTrustManagementService, workspaceTrustManagementService); // Update workspace trust so that configuration is updated accordingly diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts index cfaf8a03ac2..7e658a10cdc 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -279,7 +279,7 @@ export class DesktopMain extends Disposable { const workspaceTrustEnablementService = new WorkspaceTrustEnablementService(configurationService, environmentService); serviceCollection.set(IWorkspaceTrustEnablementService, workspaceTrustEnablementService); - const workspaceTrustManagementService = new WorkspaceTrustManagementService(configurationService, remoteAuthorityResolverService, storageService, uriIdentityService, environmentService, configurationService, workspaceTrustEnablementService, logService); + const workspaceTrustManagementService = new WorkspaceTrustManagementService(configurationService, remoteAuthorityResolverService, storageService, uriIdentityService, environmentService, configurationService, workspaceTrustEnablementService); serviceCollection.set(IWorkspaceTrustManagementService, workspaceTrustManagementService); // Update workspace trust so that configuration is updated accordingly diff --git a/src/vs/workbench/services/workspaces/common/workspaceTrust.ts b/src/vs/workbench/services/workspaces/common/workspaceTrust.ts index 8a29a82a900..e5039e9bc04 100644 --- a/src/vs/workbench/services/workspaces/common/workspaceTrust.ts +++ b/src/vs/workbench/services/workspaces/common/workspaceTrust.ts @@ -21,8 +21,7 @@ import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { isEqualAuthority } from 'vs/base/common/resources'; -import { ILogService } from 'vs/platform/log/common/log'; -import { isCI, isWeb } from 'vs/base/common/platform'; +import { isWeb } from 'vs/base/common/platform'; export const WORKSPACE_TRUST_ENABLED = 'security.workspace.trust.enabled'; export const WORKSPACE_TRUST_STARTUP_PROMPT = 'security.workspace.trust.startupPrompt'; @@ -119,8 +118,7 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService, - @IWorkspaceTrustEnablementService private readonly workspaceTrustEnablementService: IWorkspaceTrustEnablementService, - @ILogService protected readonly _logService: ILogService, + @IWorkspaceTrustEnablementService private readonly workspaceTrustEnablementService: IWorkspaceTrustEnablementService ) { super(); @@ -147,10 +145,6 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork //#region initialize private initializeWorkspaceTrust(): void { - if (isCI) { - this._logService.info(`[WT] Enter initializeWorkspaceTrust()...`); - } - // Resolve canonical Uris this.resolveCanonicalUris() .then(async () => { @@ -158,15 +152,9 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork await this.updateWorkspaceTrust(); }) .finally(() => { - if (isCI) { - this._logService.info(`[WT] Open workspaceResolved gate...`); - } this._workspaceResolvedPromiseResolve(); if (!this.environmentService.remoteAuthority) { - if (isCI) { - this._logService.info(`[WT] Open workspaceTrustInitialized gate...`); - } this._workspaceTrustInitializedPromiseResolve(); } }); @@ -211,16 +199,8 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork } private async getCanonicalUri(uri: URI): Promise { - if (isCI) { - this._logService.info('[WT] Enter getCanonicalUri()...'); - } - let canonicalUri = uri; if (this.environmentService.remoteAuthority && uri.scheme === Schemas.vscodeRemote) { - if (isCI) { - this._logService.info('[WT] Return this.remoteAuthorityResolverService.getCanonicalURI(uri)...'); - } - canonicalUri = await this.remoteAuthorityResolverService.getCanonicalURI(uri); } else if (uri.scheme === 'vscode-vfs') { const index = uri.authority.indexOf('+'); @@ -234,10 +214,6 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork } private async resolveCanonicalUris(): Promise { - if (isCI) { - this._logService.info('[WT] Enter resolveCanonicalUris()...'); - } - // Open editors const filesToOpen: IPath[] = []; if (this.environmentService.filesToOpenOrCreate) { @@ -255,32 +231,16 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork this._canonicalStartupFiles.push(...canonicalFilesToOpen.filter(uri => this._canonicalStartupFiles.every(u => !this.uriIdentityService.extUri.isEqual(uri, u)))); } - if (isCI) { - this._logService.info('[WT] Done processing open editors...'); - } - // Workspace const workspaceUris = this.workspaceService.getWorkspace().folders.map(f => f.uri); const canonicalWorkspaceFolders = await Promise.all(workspaceUris.map(uri => this.getCanonicalUri(uri))); - if (isCI) { - this._logService.info('[WT] Done processing workspace folders...'); - } - let canonicalWorkspaceConfiguration = this.workspaceService.getWorkspace().configuration; if (canonicalWorkspaceConfiguration && isSavedWorkspace(canonicalWorkspaceConfiguration, this.environmentService)) { canonicalWorkspaceConfiguration = await this.getCanonicalUri(canonicalWorkspaceConfiguration); } - if (isCI) { - this._logService.info('[WT] Done processing workspace configuration...'); - } - this._canonicalWorkspace = new CanonicalWorkspace(this.workspaceService.getWorkspace(), canonicalWorkspaceFolders, canonicalWorkspaceConfiguration); - - if (isCI) { - this._logService.info('[WT] Exit resolveCanonicalUris()...'); - } } private loadTrustInfo(): IWorkspaceTrustInfo { @@ -362,16 +322,7 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork } private async updateWorkspaceTrust(trusted?: boolean): Promise { - if (isCI) { - this._logService.info(`[WT] Enter updateWorkspaceTrust()...`); - } - if (!this.workspaceTrustEnablementService.isWorkspaceTrustEnabled()) { - if (isCI) { - this._logService.info(`[WT] Workspace trust is disabled.`); - this._logService.info(`[WT] Exit updateWorkspaceTrust()...`); - } - return; } 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 da388396897..cb8808efc50 100644 --- a/src/vs/workbench/services/workspaces/test/common/workspaceTrust.test.ts +++ b/src/vs/workbench/services/workspaces/test/common/workspaceTrust.test.ts @@ -10,7 +10,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { FileService } from 'vs/platform/files/common/fileService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { ILogService, NullLogService } from 'vs/platform/log/common/log'; +import { NullLogService } from 'vs/platform/log/common/log'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -28,7 +28,6 @@ suite('Workspace Trust', () => { let instantiationService: TestInstantiationService; let configurationService: TestConfigurationService; let environmentService: IWorkbenchEnvironmentService; - let logService: ILogService; setup(async () => { instantiationService = new TestInstantiationService(); @@ -39,10 +38,7 @@ suite('Workspace Trust', () => { environmentService = {} as IWorkbenchEnvironmentService; instantiationService.stub(IWorkbenchEnvironmentService, environmentService); - logService = new NullLogService(); - instantiationService.stub(ILogService, logService); - - instantiationService.stub(IUriIdentityService, new UriIdentityService(new FileService(logService))); + instantiationService.stub(IUriIdentityService, new UriIdentityService(new FileService(new NullLogService()))); instantiationService.stub(IRemoteAuthorityResolverService, new class extends mock() { }); }); From 856e0ae9cbc63496aecd438b8fe1923db68ea2b0 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 19 Apr 2022 14:48:46 +0200 Subject: [PATCH 134/245] Include opus files for web build --- build/gulpfile.vscode.web.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/gulpfile.vscode.web.js b/build/gulpfile.vscode.web.js index ea53c1b8c69..f0ad617f405 100644 --- a/build/gulpfile.vscode.web.js +++ b/build/gulpfile.vscode.web.js @@ -32,7 +32,7 @@ const version = (quality && quality !== 'stable') ? `${packageJson.version}-${qu const vscodeWebResourceIncludes = [ // Workbench - 'out-build/vs/{base,platform,editor,workbench}/**/*.{svg,png,jpg}', + 'out-build/vs/{base,platform,editor,workbench}/**/*.{svg,png,jpg,opus}', 'out-build/vs/code/browser/workbench/*.html', 'out-build/vs/base/browser/ui/codicons/codicon/**/*.ttf', 'out-build/vs/**/markdown.css', From a576b4c066521fe52b248eac9b21e6a01e7d22bd Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 19 Apr 2022 15:15:02 +0200 Subject: [PATCH 135/245] Fix npm task path now that there's no slash --- extensions/npm/src/tasks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/npm/src/tasks.ts b/extensions/npm/src/tasks.ts index 7ee330d1296..d05b7bbc8bf 100644 --- a/extensions/npm/src/tasks.ts +++ b/extensions/npm/src/tasks.ts @@ -67,7 +67,7 @@ export class NpmTaskProvider implements TaskProvider { return undefined; } if (kind.path) { - packageJsonUri = _task.scope.uri.with({ path: _task.scope.uri.path + '/' + kind.path + 'package.json' }); + packageJsonUri = _task.scope.uri.with({ path: _task.scope.uri.path + '/' + kind.path + `${kind.path.endsWith('/') ? '' : '/'}` + 'package.json' }); } else { packageJsonUri = _task.scope.uri.with({ path: _task.scope.uri.path + '/package.json' }); } From d1e27d8f7d1fdac8446cc6e7335286331d85c5bc Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 19 Apr 2022 15:55:59 +0200 Subject: [PATCH 136/245] Improves utility classes. --- src/vs/base/common/cache.ts | 27 +++++++++-- src/vs/base/common/errors.ts | 17 +++++++ src/vs/base/common/strings.ts | 4 +- .../supports/languageBracketsConfiguration.ts | 45 +++---------------- 4 files changed, 49 insertions(+), 44 deletions(-) diff --git a/src/vs/base/common/cache.ts b/src/vs/base/common/cache.ts index 5fac0d8f11b..1e675c36e43 100644 --- a/src/vs/base/common/cache.ts +++ b/src/vs/base/common/cache.ts @@ -41,19 +41,40 @@ export class Cache { * Caches just the last value. * The key must be JSON serializable. */ -export class LRUCachedComputed { +export class LRUCachedFunction { private lastCache: TComputed | undefined = undefined; private lastArgKey: string | undefined = undefined; - constructor(private readonly computeFn: (arg: TArg) => TComputed) { + constructor(private readonly fn: (arg: TArg) => TComputed) { } public get(arg: TArg): TComputed { const key = JSON.stringify(arg); if (this.lastArgKey !== key) { this.lastArgKey = key; - this.lastCache = this.computeFn(arg); + this.lastCache = this.fn(arg); } return this.lastCache!; } } + +/** + * Uses an unbounded cache (referential equality) to memoize the results of the given function. +*/ +export class CachedFunction { + private readonly _map = new Map(); + public get cachedValues(): ReadonlyMap { + return this._map; + } + + constructor(private readonly fn: (arg: TArg) => TValue) { } + + public get(arg: TArg): TValue { + if (this._map.has(arg)) { + return this._map.get(arg)!; + } + const value = this.fn(arg); + this._map.set(arg, value); + return value; + } +} diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index 08528474361..91417305378 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -248,3 +248,20 @@ export class ErrorNoTelemetry extends Error { readonly logTelemetry = false; } + +/** + * This error indicates a bug. + * Do not throw this for invalid user input. + * Only catch this error to recover gracefully from bugs. + */ +export class BugIndicatingError extends Error { + constructor(message: string) { + super(message); + Object.setPrototypeOf(this, BugIndicatingError.prototype); + + // Because we know for sure only buggy code throws this, + // we definitely want to break here and fix the bug. + // eslint-disable-next-line no-debugger + debugger; + } +} diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index 8aa9e50c9e9..62fddd69f91 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { LRUCachedComputed } from 'vs/base/common/cache'; +import { LRUCachedFunction } from 'vs/base/common/cache'; import { CharCode } from 'vs/base/common/charCode'; import { Lazy } from 'vs/base/common/lazy'; import { Constants } from 'vs/base/common/uint'; @@ -1075,7 +1075,7 @@ export class AmbiguousCharacters { ); }); - private static readonly cache = new LRUCachedComputed< + private static readonly cache = new LRUCachedFunction< string[], AmbiguousCharacters >((locales) => { diff --git a/src/vs/editor/common/languages/supports/languageBracketsConfiguration.ts b/src/vs/editor/common/languages/supports/languageBracketsConfiguration.ts index 4229b5aa3a0..79f92c6ed52 100644 --- a/src/vs/editor/common/languages/supports/languageBracketsConfiguration.ts +++ b/src/vs/editor/common/languages/supports/languageBracketsConfiguration.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CachedFunction } from 'vs/base/common/cache'; +import { BugIndicatingError } from 'vs/base/common/errors'; import { LanguageConfiguration } from 'vs/editor/common/languages/languageConfiguration'; /** @@ -35,14 +37,14 @@ export class LanguageBracketsConfiguration { brackets = []; } - const openingBracketInfos = new LazyMap((bracket: string) => { + const openingBracketInfos = new CachedFunction((bracket: string) => { const closing = new Set(); return { info: new OpeningBracketKind(this, bracket, closing), closing, }; }); - const closingBracketInfos = new LazyMap((bracket: string) => { + const closingBracketInfos = new CachedFunction((bracket: string) => { const opening = new Set(); return { info: new ClosingBracketKind(this, bracket, opening), @@ -58,8 +60,8 @@ export class LanguageBracketsConfiguration { closing.opening.add(opening.info); } - this._openingBrackets = new Map([...openingBracketInfos.innerMap].map(([k, v]) => [k, v.info])); - this._closingBrackets = new Map([...closingBracketInfos.innerMap].map(([k, v]) => [k, v.info])); + this._openingBrackets = new Map([...openingBracketInfos.cachedValues].map(([k, v]) => [k, v.info])); + this._closingBrackets = new Map([...closingBracketInfos.cachedValues].map(([k, v]) => [k, v.info])); } /** @@ -151,38 +153,3 @@ export class ClosingBracketKind extends BracketKindBase { return [...this.closedBrackets]; } } - -// Utilities - -/** - * This error indicates a bug. - */ -class BugIndicatingError extends Error { - constructor(message: string) { - super(message); - Object.setPrototypeOf(this, BugIndicatingError.prototype); - - // Because we know for sure only buggy code throws this, - // we definitely want to break here and fix the bug. - // eslint-disable-next-line no-debugger - debugger; - } -} - -class LazyMap { - private readonly _map = new Map(); - public get innerMap(): ReadonlyMap { - return this._map; - } - - constructor(private readonly initialize: (key: TKey) => TValue) { } - - public get(key: TKey): TValue { - if (this._map.has(key)) { - return this._map.get(key)!; - } - const value = this.initialize(key); - this._map.set(key, value); - return value; - } -} From 90c40f08ab76072f400a678c07ef5aec15856bb8 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 19 Apr 2022 16:24:41 +0200 Subject: [PATCH 137/245] Fixes #146524 --- .../inlineCompletions/browser/inlineCompletionsModel.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts index bccd1734dbf..5a99aa34773 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts @@ -403,10 +403,14 @@ export class InlineCompletionsSession extends BaseGhostTextWidgetModel { if (!currentCompletion) { return undefined; } + const cursorPosition = this.editor.getPosition(); + if (currentCompletion.range.getEndPosition().isBefore(cursorPosition)) { + return undefined; + } const mode = this.editor.getOptions().get(EditorOption.inlineSuggest).mode; - const ghostText = inlineCompletionToGhostText(currentCompletion, this.editor.getModel(), mode, this.editor.getPosition()); + const ghostText = inlineCompletionToGhostText(currentCompletion, this.editor.getModel(), mode, cursorPosition); if (ghostText) { if (ghostText.isEmpty()) { return undefined; From 9be47ee6abe10309e80b1248621bbcdd64c20dd8 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Tue, 19 Apr 2022 10:32:21 -0400 Subject: [PATCH 138/245] Update server telemetry --- .../common/serverTelemetryService.ts | 41 +------------------ .../server/node/serverEnvironmentService.ts | 2 +- src/vs/server/node/serverServices.ts | 2 +- 3 files changed, 4 insertions(+), 41 deletions(-) diff --git a/src/vs/platform/telemetry/common/serverTelemetryService.ts b/src/vs/platform/telemetry/common/serverTelemetryService.ts index 48dfb1cc8fe..74d12ea8086 100644 --- a/src/vs/platform/telemetry/common/serverTelemetryService.ts +++ b/src/vs/platform/telemetry/common/serverTelemetryService.ts @@ -15,22 +15,14 @@ export interface IServerTelemetryService extends ITelemetryService { updateInjectedTelemetryLevel(telemetryLevel: TelemetryLevel): Promise; } -interface CachedTelemetryEvent { - eventName: string; - data?: ITelemetryData; - anonymizeFilePaths?: boolean; - eventType: 'usage' | 'error'; -} - export class ServerTelemetryService extends TelemetryService implements IServerTelemetryService { - private _telemetryCache: CachedTelemetryEvent[] = []; // Because we cannot read the workspace config on the remote site // the ServerTelemetryService is responsible for knowing its telemetry level // this is done through IPC calls and initial value injections - private _injectedTelemetryLevel: TelemetryLevel | undefined; + private _injectedTelemetryLevel: TelemetryLevel; constructor( config: ITelemetryServiceConfig, - injectedTelemetryLevel: TelemetryLevel | undefined, + injectedTelemetryLevel: TelemetryLevel, @IConfigurationService _configurationService: IConfigurationService, @IProductService _productService: IProductService ) { @@ -39,11 +31,6 @@ export class ServerTelemetryService extends TelemetryService implements IServerT } override publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): Promise { - if (this._injectedTelemetryLevel === undefined) { - // Undefined safety with cache in case super class calls log before cache is initialized in subclass constructor - this._telemetryCache?.push({ eventName, data, anonymizeFilePaths, eventType: 'usage' }); - return Promise.resolve(); - } if (this._injectedTelemetryLevel < TelemetryLevel.USAGE) { return Promise.resolve(undefined); } @@ -55,11 +42,6 @@ export class ServerTelemetryService extends TelemetryService implements IServerT } override publicLogError(errorEventName: string, data?: ITelemetryData): Promise { - if (this._injectedTelemetryLevel === undefined) { - // Undefined safety with cache in case super class calls log before cache is initialized in subclass constructor - this._telemetryCache?.push({ eventName: errorEventName, data, eventType: 'error' }); - return Promise.resolve(); - } if (this._injectedTelemetryLevel < TelemetryLevel.ERROR) { return Promise.resolve(undefined); } @@ -70,21 +52,6 @@ export class ServerTelemetryService extends TelemetryService implements IServerT return this.publicLogError(eventName, data as ITelemetryData | undefined); } - // Flushes all the cached events with the new level - async flushTelemetryCache(): Promise { - if (this._telemetryCache?.length === 0) { - return; - } - for (const cacheItem of this._telemetryCache) { - if (cacheItem.eventType === 'usage') { - await this.publicLog(cacheItem.eventName, cacheItem.data, cacheItem.anonymizeFilePaths); - } else { - await this.publicLogError(cacheItem.eventName, cacheItem.data); - } - } - this._telemetryCache = []; - } - async updateInjectedTelemetryLevel(telemetryLevel: TelemetryLevel): Promise { if (telemetryLevel === undefined) { this._injectedTelemetryLevel = TelemetryLevel.NONE; @@ -93,11 +60,7 @@ export class ServerTelemetryService extends TelemetryService implements IServerT // We always take the most restrictive level because we don't want multiple clients to connect and send data when one client does not consent this._injectedTelemetryLevel = this._injectedTelemetryLevel ? Math.min(this._injectedTelemetryLevel, telemetryLevel) : telemetryLevel; if (this._injectedTelemetryLevel === TelemetryLevel.NONE) { - this._telemetryCache = []; this.dispose(); - } else { - // Level was set we're no longer in a pending state we flush the telemetry cache. - return this.flushTelemetryCache(); } } } diff --git a/src/vs/server/node/serverEnvironmentService.ts b/src/vs/server/node/serverEnvironmentService.ts index b94d231c5d5..f291461470d 100644 --- a/src/vs/server/node/serverEnvironmentService.ts +++ b/src/vs/server/node/serverEnvironmentService.ts @@ -26,7 +26,7 @@ export const serverOptions: OptionDescriptions = { 'print-ip-address': { type: 'boolean' }, 'accept-server-license-terms': { type: 'boolean', cat: 'o', description: nls.localize('acceptLicenseTerms', "If set, the user accepts the server license terms and the server will be started without a user prompt.") }, 'server-data-dir': { type: 'string', cat: 'o', description: nls.localize('serverDataDir', "Specifies the directory that server data is kept in.") }, - 'telemetry-level': { type: 'string', cat: 'o', args: 'level', description: nls.localize('telemetry-level', "Sets the initial telemetry level. Valid levels are: 'off', 'crash', 'error' and 'all'. If not specified, the server will await a connection before sending any telemetry. Setting this to 'off' is equivalent to --disable-telemetry") }, + 'telemetry-level': { type: 'string', cat: 'o', args: 'level', description: nls.localize('telemetry-level', "Sets the initial telemetry level. Valid levels are: 'off', 'crash', 'error' and 'all'. If not specified, the server will send telemetry until a client connects, it will then use the clients telemetry setting. Setting this to 'off' is equivalent to --disable-telemetry") }, /* ----- vs code options --- -- */ diff --git a/src/vs/server/node/serverServices.ts b/src/vs/server/node/serverServices.ts index 8e14d6fdc2b..a072fa8bb0e 100644 --- a/src/vs/server/node/serverServices.ts +++ b/src/vs/server/node/serverServices.ts @@ -133,7 +133,7 @@ export async function setupServerServices(connectionToken: ServerConnectionToken piiPaths: getPiiPathsFromEnvironment(environmentService) }; const initialTelemetryLevelArg = environmentService.args['telemetry-level']; - let injectedTelemetryLevel: TelemetryLevel | undefined = undefined; + let injectedTelemetryLevel: TelemetryLevel = TelemetryLevel.USAGE; // Convert the passed in CLI argument into a telemetry level for the telemetry service if (initialTelemetryLevelArg === 'all') { injectedTelemetryLevel = TelemetryLevel.USAGE; From bc5bfa062158cf598ad568202c044cf566deddf0 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 19 Apr 2022 16:43:26 +0200 Subject: [PATCH 139/245] Fixes 146816 --- .../inlineCompletions/browser/inlineCompletionsModel.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts index 5a99aa34773..e7870b9c77d 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts @@ -569,6 +569,7 @@ export class UpdateOperation implements IDisposable { */ export class SynchronizedInlineCompletionsCache extends Disposable { public readonly completions: readonly CachedInlineCompletion[]; + private isDisposing = false; constructor( completionsSource: TrackedInlineCompletions, @@ -588,6 +589,7 @@ export class SynchronizedInlineCompletionsCache extends Disposable { })) ); this._register(toDisposable(() => { + this.isDisposing = true; editor.deltaDecorations(decorationIds, []); })); @@ -600,7 +602,11 @@ export class SynchronizedInlineCompletionsCache extends Disposable { this._register(completionsSource); } - public updateRanges() { + public updateRanges(): void { + if (this.isDisposing) { + return; + } + let hasChanged = false; const model = this.editor.getModel(); for (const c of this.completions) { From 8edff91035f2752efb2222ca91790c3162ee8b87 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 19 Apr 2022 16:52:36 +0200 Subject: [PATCH 140/245] Improves code. --- .../inlineCompletions/browser/ghostTextWidget.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/ghostTextWidget.ts b/src/vs/editor/contrib/inlineCompletions/browser/ghostTextWidget.ts index ee134296862..d6095291d4c 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/ghostTextWidget.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/ghostTextWidget.ts @@ -81,7 +81,7 @@ export class GhostTextWidget extends Disposable { if (!this.editor.hasModel() || !ghostText || this.disposed) { this.partsWidget.clear(); this.additionalLinesWidget.clear(); - this.replacementDecoration.setDecorations([]); + this.replacementDecoration.clear(); return; } @@ -213,8 +213,14 @@ class DisposableDecorations { public setDecorations(decorations: IModelDeltaDecoration[]): void { this.decorationIds = this.editor.deltaDecorations(this.decorationIds, decorations); } - public dispose(): void { + + public clear() { this.editor.deltaDecorations(this.decorationIds, []); + this.decorationIds = []; + } + + public dispose(): void { + this.clear(); } } From 29083878d662e0fea388cbe6aac989952141504c Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 19 Apr 2022 17:24:03 +0200 Subject: [PATCH 141/245] refresh inlay hints immediately after applying inlay hint edits, fixes https://github.com/microsoft/vscode/issues/147663 --- .../contrib/inlayHints/browser/inlayHintsController.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/inlayHints/browser/inlayHintsController.ts b/src/vs/editor/contrib/inlayHints/browser/inlayHintsController.ts index 85d07db70d7..7adeb41f7f1 100644 --- a/src/vs/editor/contrib/inlayHints/browser/inlayHintsController.ts +++ b/src/vs/editor/contrib/inlayHints/browser/inlayHintsController.ts @@ -258,8 +258,8 @@ export class InlayHintsController implements IEditorContribution { } // mouse gestures + this._sessionDisposables.add(this._installDblClickGesture(() => scheduler.schedule(0))); this._sessionDisposables.add(this._installLinkGesture()); - this._sessionDisposables.add(this._installDblClickGesture()); this._sessionDisposables.add(this._installContextMenu()); } @@ -329,7 +329,7 @@ export class InlayHintsController implements IEditorContribution { return Array.from(lineHints); } - private _installDblClickGesture(): IDisposable { + private _installDblClickGesture(updateInlayHints: Function): IDisposable { return this._editor.onMouseUp(async e => { if (e.event.detail !== 2) { return; @@ -343,6 +343,7 @@ export class InlayHintsController implements IEditorContribution { if (isNonEmptyArray(part.item.hint.textEdits)) { const edits = part.item.hint.textEdits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text)); this._editor.executeEdits('inlayHint.default', edits); + updateInlayHints(); } }); } From 56fcb7c7c8715c8496285a036aaaa71e56e9a176 Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Tue, 19 Apr 2022 08:33:07 -0700 Subject: [PATCH 142/245] Polish RPM dependencies generator (#147336) * Print dependencies * Add reference deps for rpm package * Add flag to control new deps behaviour * Add dependencies by arch * Link arch properly --- build/gulpfile.vscode.linux.js | 2 +- build/linux/rpm/dep-lists.js | 280 +++++++++++++++++++++- build/linux/rpm/dep-lists.ts | 279 +++++++++++++++++++++ build/linux/rpm/dependencies-generator.js | 26 +- build/linux/rpm/dependencies-generator.ts | 32 ++- build/linux/rpm/types.js | 6 + build/linux/rpm/types.ts | 6 + 7 files changed, 618 insertions(+), 13 deletions(-) create mode 100644 build/linux/rpm/types.js create mode 100644 build/linux/rpm/types.ts diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index 3c6a7b3ee89..7d0f70f9bef 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -176,7 +176,7 @@ function prepareRpmPackage(arch) { const code = gulp.src(binaryDir + '/**/*', { base: binaryDir }) .pipe(rename(function (p) { p.dirname = 'BUILD/usr/share/' + product.applicationName + '/' + p.dirname; })); - const dependencies = rpmDependenciesGenerator.getDependencies(binaryDir, product.applicationName); + const dependencies = rpmDependenciesGenerator.getDependencies(binaryDir, product.applicationName, rpmArch); const spec = gulp.src('resources/linux/rpm/code.spec.template', { base: '.' }) .pipe(replace('@@NAME@@', product.applicationName)) .pipe(replace('@@NAME_LONG@@', product.nameLong)) diff --git a/build/linux/rpm/dep-lists.js b/build/linux/rpm/dep-lists.js index f72b442670a..99064a1f7e6 100644 --- a/build/linux/rpm/dep-lists.js +++ b/build/linux/rpm/dep-lists.js @@ -4,7 +4,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ Object.defineProperty(exports, "__esModule", { value: true }); -exports.bundledDeps = exports.additionalDeps = void 0; +exports.referenceGeneratedDepsByArch = exports.bundledDeps = exports.additionalDeps = void 0; // Based on https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/rpm/additional_deps // Additional dependencies not in the rpm find-requires output. exports.additionalDeps = [ @@ -29,3 +29,281 @@ exports.bundledDeps = [ 'libvk_swiftshader.so', 'libffmpeg.so' ]; +exports.referenceGeneratedDepsByArch = { + 'x86_64': [ + 'ca-certificates', + 'ld-linux-x86-64.so.2()(64bit)', + 'ld-linux-x86-64.so.2(GLIBC_2.2.5)(64bit)', + 'ld-linux-x86-64.so.2(GLIBC_2.3)(64bit)', + 'libX11.so.6()(64bit)', + 'libXcomposite.so.1()(64bit)', + 'libXdamage.so.1()(64bit)', + 'libXext.so.6()(64bit)', + 'libXfixes.so.3()(64bit)', + 'libXrandr.so.2()(64bit)', + 'libasound.so.2()(64bit)', + 'libasound.so.2(ALSA_0.9)(64bit)', + 'libasound.so.2(ALSA_0.9.0rc4)(64bit)', + 'libatk-1.0.so.0()(64bit)', + 'libatk-bridge-2.0.so.0()(64bit)', + 'libatspi.so.0()(64bit)', + 'libc.so.6()(64bit)', + 'libc.so.6(GLIBC_2.10)(64bit)', + 'libc.so.6(GLIBC_2.11)(64bit)', + 'libc.so.6(GLIBC_2.14)(64bit)', + 'libc.so.6(GLIBC_2.15)(64bit)', + 'libc.so.6(GLIBC_2.16)(64bit)', + 'libc.so.6(GLIBC_2.17)(64bit)', + 'libc.so.6(GLIBC_2.2.5)(64bit)', + 'libc.so.6(GLIBC_2.3)(64bit)', + 'libc.so.6(GLIBC_2.3.2)(64bit)', + 'libc.so.6(GLIBC_2.3.3)(64bit)', + 'libc.so.6(GLIBC_2.3.4)(64bit)', + 'libc.so.6(GLIBC_2.4)(64bit)', + 'libc.so.6(GLIBC_2.6)(64bit)', + 'libc.so.6(GLIBC_2.7)(64bit)', + 'libc.so.6(GLIBC_2.8)(64bit)', + 'libc.so.6(GLIBC_2.9)(64bit)', + 'libcairo.so.2()(64bit)', + 'libcurl.so.4()(64bit)', + 'libdbus-1.so.3()(64bit)', + 'libdl.so.2()(64bit)', + 'libdl.so.2(GLIBC_2.2.5)(64bit)', + 'libdrm.so.2()(64bit)', + 'libexpat.so.1()(64bit)', + 'libgbm.so.1()(64bit)', + 'libgcc_s.so.1()(64bit)', + 'libgcc_s.so.1(GCC_3.0)(64bit)', + 'libgdk_pixbuf-2.0.so.0()(64bit)', + 'libgio-2.0.so.0()(64bit)', + 'libglib-2.0.so.0()(64bit)', + 'libgmodule-2.0.so.0()(64bit)', + 'libgobject-2.0.so.0()(64bit)', + 'libgtk-3.so.0()(64bit)', + 'libm.so.6()(64bit)', + 'libm.so.6(GLIBC_2.2.5)(64bit)', + 'libnspr4.so()(64bit)', + 'libnss3.so()(64bit)', + 'libnss3.so(NSS_3.11)(64bit)', + 'libnss3.so(NSS_3.12)(64bit)', + 'libnss3.so(NSS_3.12.1)(64bit)', + 'libnss3.so(NSS_3.2)(64bit)', + 'libnss3.so(NSS_3.22)(64bit)', + 'libnss3.so(NSS_3.3)(64bit)', + 'libnss3.so(NSS_3.4)(64bit)', + 'libnss3.so(NSS_3.5)(64bit)', + 'libnss3.so(NSS_3.9.2)(64bit)', + 'libnssutil3.so()(64bit)', + 'libnssutil3.so(NSSUTIL_3.12.3)(64bit)', + 'libpango-1.0.so.0()(64bit)', + 'libpthread.so.0()(64bit)', + 'libpthread.so.0(GLIBC_2.12)(64bit)', + 'libpthread.so.0(GLIBC_2.2.5)(64bit)', + 'libpthread.so.0(GLIBC_2.3.2)(64bit)', + 'libpthread.so.0(GLIBC_2.3.3)(64bit)', + 'libpthread.so.0(GLIBC_2.3.4)(64bit)', + 'librt.so.1()(64bit)', + 'librt.so.1(GLIBC_2.2.5)(64bit)', + 'libsecret-1.so.0()(64bit)', + 'libsmime3.so()(64bit)', + 'libsmime3.so(NSS_3.10)(64bit)', + 'libsmime3.so(NSS_3.2)(64bit)', + 'libssl3.so(NSS_3.28)(64bit)', + 'libutil.so.1()(64bit)', + 'libutil.so.1(GLIBC_2.2.5)(64bit)', + 'libxcb.so.1()(64bit)', + 'libxkbcommon.so.0()(64bit)', + 'libxkbfile.so.1()(64bit)', + 'rpmlib(FileDigests) <= 4.6.0-1', + 'rtld(GNU_HASH)', + 'xdg-utils' + ], + 'armv7hl': [ + 'ca-certificates', + 'ld-linux-armhf.so.3', + 'ld-linux-armhf.so.3(GLIBC_2.4)', + 'libX11.so.6', + 'libXcomposite.so.1', + 'libXdamage.so.1', + 'libXext.so.6', + 'libXfixes.so.3', + 'libXrandr.so.2', + 'libasound.so.2', + 'libasound.so.2(ALSA_0.9)', + 'libasound.so.2(ALSA_0.9.0rc4)', + 'libatk-1.0.so.0', + 'libatk-bridge-2.0.so.0', + 'libatspi.so.0', + 'libc.so.6', + 'libc.so.6(GLIBC_2.10)', + 'libc.so.6(GLIBC_2.11)', + 'libc.so.6(GLIBC_2.14)', + 'libc.so.6(GLIBC_2.15)', + 'libc.so.6(GLIBC_2.16)', + 'libc.so.6(GLIBC_2.17)', + 'libc.so.6(GLIBC_2.4)', + 'libc.so.6(GLIBC_2.6)', + 'libc.so.6(GLIBC_2.7)', + 'libc.so.6(GLIBC_2.8)', + 'libc.so.6(GLIBC_2.9)', + 'libcairo.so.2', + 'libcurl.so.4()(64bit)', + 'libdbus-1.so.3', + 'libdl.so.2', + 'libdl.so.2(GLIBC_2.4)', + 'libdrm.so.2', + 'libexpat.so.1', + 'libgbm.so.1', + 'libgcc_s.so.1', + 'libgcc_s.so.1(GCC_3.0)', + 'libgcc_s.so.1(GCC_3.4)', + 'libgcc_s.so.1(GCC_3.5)', + 'libgdk_pixbuf-2.0.so.0', + 'libgio-2.0.so.0', + 'libglib-2.0.so.0', + 'libgmodule-2.0.so.0', + 'libgobject-2.0.so.0', + 'libgtk-3.so.0', + 'libgtk-3.so.0()(64bit)', + 'libm.so.6', + 'libm.so.6(GLIBC_2.4)', + 'libnspr4.so', + 'libnss3.so', + 'libnss3.so(NSS_3.11)', + 'libnss3.so(NSS_3.12)', + 'libnss3.so(NSS_3.12.1)', + 'libnss3.so(NSS_3.2)', + 'libnss3.so(NSS_3.22)', + 'libnss3.so(NSS_3.22)(64bit)', + 'libnss3.so(NSS_3.3)', + 'libnss3.so(NSS_3.4)', + 'libnss3.so(NSS_3.5)', + 'libnss3.so(NSS_3.9.2)', + 'libnssutil3.so', + 'libnssutil3.so(NSSUTIL_3.12.3)', + 'libpango-1.0.so.0', + 'libpthread.so.0', + 'libpthread.so.0(GLIBC_2.12)', + 'libpthread.so.0(GLIBC_2.4)', + 'librt.so.1', + 'librt.so.1(GLIBC_2.4)', + 'libsecret-1.so.0', + 'libsmime3.so', + 'libsmime3.so(NSS_3.10)', + 'libsmime3.so(NSS_3.2)', + 'libssl3.so(NSS_3.28)(64bit)', + 'libstdc++.so.6', + 'libstdc++.so.6(CXXABI_1.3)', + 'libstdc++.so.6(CXXABI_1.3.5)', + 'libstdc++.so.6(CXXABI_1.3.8)', + 'libstdc++.so.6(CXXABI_1.3.9)', + 'libstdc++.so.6(CXXABI_ARM_1.3.3)', + 'libstdc++.so.6(GLIBCXX_3.4)', + 'libstdc++.so.6(GLIBCXX_3.4.11)', + 'libstdc++.so.6(GLIBCXX_3.4.14)', + 'libstdc++.so.6(GLIBCXX_3.4.15)', + 'libstdc++.so.6(GLIBCXX_3.4.18)', + 'libstdc++.so.6(GLIBCXX_3.4.19)', + 'libstdc++.so.6(GLIBCXX_3.4.20)', + 'libstdc++.so.6(GLIBCXX_3.4.21)', + 'libstdc++.so.6(GLIBCXX_3.4.22)', + 'libstdc++.so.6(GLIBCXX_3.4.5)', + 'libstdc++.so.6(GLIBCXX_3.4.9)', + 'libutil.so.1', + 'libutil.so.1(GLIBC_2.4)', + 'libxcb.so.1', + 'libxkbcommon.so.0', + 'libxkbfile.so.1', + 'rpmlib(FileDigests) <= 4.6.0-1', + 'rtld(GNU_HASH)', + 'xdg-utils' + ], + 'aarch64': [ + 'ca-certificates', + 'ld-linux-aarch64.so.1()(64bit)', + 'ld-linux-aarch64.so.1(GLIBC_2.17)(64bit)', + 'libX11.so.6()(64bit)', + 'libXcomposite.so.1()(64bit)', + 'libXdamage.so.1()(64bit)', + 'libXext.so.6()(64bit)', + 'libXfixes.so.3()(64bit)', + 'libXrandr.so.2()(64bit)', + 'libasound.so.2()(64bit)', + 'libasound.so.2(ALSA_0.9)(64bit)', + 'libasound.so.2(ALSA_0.9.0rc4)(64bit)', + 'libatk-1.0.so.0()(64bit)', + 'libatk-bridge-2.0.so.0()(64bit)', + 'libatspi.so.0()(64bit)', + 'libc.so.6()(64bit)', + 'libc.so.6(GLIBC_2.17)(64bit)', + 'libcairo.so.2()(64bit)', + 'libcurl.so.4()(64bit)', + 'libdbus-1.so.3()(64bit)', + 'libdbus-1.so.3(LIBDBUS_1_3)(64bit)', + 'libdl.so.2()(64bit)', + 'libdl.so.2(GLIBC_2.17)(64bit)', + 'libdrm.so.2()(64bit)', + 'libexpat.so.1()(64bit)', + 'libgbm.so.1()(64bit)', + 'libgcc_s.so.1()(64bit)', + 'libgcc_s.so.1(GCC_3.0)(64bit)', + 'libgcc_s.so.1(GCC_4.2.0)(64bit)', + 'libgcc_s.so.1(GCC_4.5.0)(64bit)', + 'libgdk_pixbuf-2.0.so.0()(64bit)', + 'libgio-2.0.so.0()(64bit)', + 'libglib-2.0.so.0()(64bit)', + 'libgmodule-2.0.so.0()(64bit)', + 'libgobject-2.0.so.0()(64bit)', + 'libgtk-3.so.0()(64bit)', + 'libm.so.6()(64bit)', + 'libm.so.6(GLIBC_2.17)(64bit)', + 'libnspr4.so()(64bit)', + 'libnss3.so()(64bit)', + 'libnss3.so(NSS_3.11)(64bit)', + 'libnss3.so(NSS_3.12)(64bit)', + 'libnss3.so(NSS_3.12.1)(64bit)', + 'libnss3.so(NSS_3.2)(64bit)', + 'libnss3.so(NSS_3.22)(64bit)', + 'libnss3.so(NSS_3.3)(64bit)', + 'libnss3.so(NSS_3.4)(64bit)', + 'libnss3.so(NSS_3.5)(64bit)', + 'libnss3.so(NSS_3.9.2)(64bit)', + 'libnssutil3.so()(64bit)', + 'libnssutil3.so(NSSUTIL_3.12.3)(64bit)', + 'libpango-1.0.so.0()(64bit)', + 'libpthread.so.0()(64bit)', + 'libpthread.so.0(GLIBC_2.17)(64bit)', + 'librt.so.1()(64bit)', + 'librt.so.1(GLIBC_2.17)(64bit)', + 'libsecret-1.so.0()(64bit)', + 'libsmime3.so()(64bit)', + 'libsmime3.so(NSS_3.10)(64bit)', + 'libsmime3.so(NSS_3.2)(64bit)', + 'libssl3.so(NSS_3.28)(64bit)', + 'libstdc++.so.6()(64bit)', + 'libstdc++.so.6(CXXABI_1.3)(64bit)', + 'libstdc++.so.6(CXXABI_1.3.5)(64bit)', + 'libstdc++.so.6(CXXABI_1.3.8)(64bit)', + 'libstdc++.so.6(CXXABI_1.3.9)(64bit)', + 'libstdc++.so.6(GLIBCXX_3.4)(64bit)', + 'libstdc++.so.6(GLIBCXX_3.4.11)(64bit)', + 'libstdc++.so.6(GLIBCXX_3.4.14)(64bit)', + 'libstdc++.so.6(GLIBCXX_3.4.15)(64bit)', + 'libstdc++.so.6(GLIBCXX_3.4.18)(64bit)', + 'libstdc++.so.6(GLIBCXX_3.4.19)(64bit)', + 'libstdc++.so.6(GLIBCXX_3.4.20)(64bit)', + 'libstdc++.so.6(GLIBCXX_3.4.21)(64bit)', + 'libstdc++.so.6(GLIBCXX_3.4.22)(64bit)', + 'libstdc++.so.6(GLIBCXX_3.4.5)(64bit)', + 'libstdc++.so.6(GLIBCXX_3.4.9)(64bit)', + 'libutil.so.1()(64bit)', + 'libutil.so.1(GLIBC_2.17)(64bit)', + 'libxcb.so.1()(64bit)', + 'libxkbcommon.so.0()(64bit)', + 'libxkbcommon.so.0(V_0.5.0)(64bit)', + 'libxkbfile.so.1()(64bit)', + 'rpmlib(FileDigests) <= 4.6.0-1', + 'rtld(GNU_HASH)', + 'xdg-utils' + ] +}; diff --git a/build/linux/rpm/dep-lists.ts b/build/linux/rpm/dep-lists.ts index 884f6825fbd..524cc0d7edd 100644 --- a/build/linux/rpm/dep-lists.ts +++ b/build/linux/rpm/dep-lists.ts @@ -28,3 +28,282 @@ export const bundledDeps = [ 'libvk_swiftshader.so', 'libffmpeg.so' ]; + +export const referenceGeneratedDepsByArch = { + 'x86_64': [ + 'ca-certificates', + 'ld-linux-x86-64.so.2()(64bit)', + 'ld-linux-x86-64.so.2(GLIBC_2.2.5)(64bit)', + 'ld-linux-x86-64.so.2(GLIBC_2.3)(64bit)', + 'libX11.so.6()(64bit)', + 'libXcomposite.so.1()(64bit)', + 'libXdamage.so.1()(64bit)', + 'libXext.so.6()(64bit)', + 'libXfixes.so.3()(64bit)', + 'libXrandr.so.2()(64bit)', + 'libasound.so.2()(64bit)', + 'libasound.so.2(ALSA_0.9)(64bit)', + 'libasound.so.2(ALSA_0.9.0rc4)(64bit)', + 'libatk-1.0.so.0()(64bit)', + 'libatk-bridge-2.0.so.0()(64bit)', + 'libatspi.so.0()(64bit)', + 'libc.so.6()(64bit)', + 'libc.so.6(GLIBC_2.10)(64bit)', + 'libc.so.6(GLIBC_2.11)(64bit)', + 'libc.so.6(GLIBC_2.14)(64bit)', + 'libc.so.6(GLIBC_2.15)(64bit)', + 'libc.so.6(GLIBC_2.16)(64bit)', + 'libc.so.6(GLIBC_2.17)(64bit)', + 'libc.so.6(GLIBC_2.2.5)(64bit)', + 'libc.so.6(GLIBC_2.3)(64bit)', + 'libc.so.6(GLIBC_2.3.2)(64bit)', + 'libc.so.6(GLIBC_2.3.3)(64bit)', + 'libc.so.6(GLIBC_2.3.4)(64bit)', + 'libc.so.6(GLIBC_2.4)(64bit)', + 'libc.so.6(GLIBC_2.6)(64bit)', + 'libc.so.6(GLIBC_2.7)(64bit)', + 'libc.so.6(GLIBC_2.8)(64bit)', + 'libc.so.6(GLIBC_2.9)(64bit)', + 'libcairo.so.2()(64bit)', + 'libcurl.so.4()(64bit)', + 'libdbus-1.so.3()(64bit)', + 'libdl.so.2()(64bit)', + 'libdl.so.2(GLIBC_2.2.5)(64bit)', + 'libdrm.so.2()(64bit)', + 'libexpat.so.1()(64bit)', + 'libgbm.so.1()(64bit)', + 'libgcc_s.so.1()(64bit)', + 'libgcc_s.so.1(GCC_3.0)(64bit)', + 'libgdk_pixbuf-2.0.so.0()(64bit)', + 'libgio-2.0.so.0()(64bit)', + 'libglib-2.0.so.0()(64bit)', + 'libgmodule-2.0.so.0()(64bit)', + 'libgobject-2.0.so.0()(64bit)', + 'libgtk-3.so.0()(64bit)', + 'libm.so.6()(64bit)', + 'libm.so.6(GLIBC_2.2.5)(64bit)', + 'libnspr4.so()(64bit)', + 'libnss3.so()(64bit)', + 'libnss3.so(NSS_3.11)(64bit)', + 'libnss3.so(NSS_3.12)(64bit)', + 'libnss3.so(NSS_3.12.1)(64bit)', + 'libnss3.so(NSS_3.2)(64bit)', + 'libnss3.so(NSS_3.22)(64bit)', + 'libnss3.so(NSS_3.3)(64bit)', + 'libnss3.so(NSS_3.4)(64bit)', + 'libnss3.so(NSS_3.5)(64bit)', + 'libnss3.so(NSS_3.9.2)(64bit)', + 'libnssutil3.so()(64bit)', + 'libnssutil3.so(NSSUTIL_3.12.3)(64bit)', + 'libpango-1.0.so.0()(64bit)', + 'libpthread.so.0()(64bit)', + 'libpthread.so.0(GLIBC_2.12)(64bit)', + 'libpthread.so.0(GLIBC_2.2.5)(64bit)', + 'libpthread.so.0(GLIBC_2.3.2)(64bit)', + 'libpthread.so.0(GLIBC_2.3.3)(64bit)', + 'libpthread.so.0(GLIBC_2.3.4)(64bit)', + 'librt.so.1()(64bit)', + 'librt.so.1(GLIBC_2.2.5)(64bit)', + 'libsecret-1.so.0()(64bit)', + 'libsmime3.so()(64bit)', + 'libsmime3.so(NSS_3.10)(64bit)', + 'libsmime3.so(NSS_3.2)(64bit)', + 'libssl3.so(NSS_3.28)(64bit)', + 'libutil.so.1()(64bit)', + 'libutil.so.1(GLIBC_2.2.5)(64bit)', + 'libxcb.so.1()(64bit)', + 'libxkbcommon.so.0()(64bit)', + 'libxkbfile.so.1()(64bit)', + 'rpmlib(FileDigests) <= 4.6.0-1', + 'rtld(GNU_HASH)', + 'xdg-utils' + ], + 'armv7hl': [ + 'ca-certificates', + 'ld-linux-armhf.so.3', + 'ld-linux-armhf.so.3(GLIBC_2.4)', + 'libX11.so.6', + 'libXcomposite.so.1', + 'libXdamage.so.1', + 'libXext.so.6', + 'libXfixes.so.3', + 'libXrandr.so.2', + 'libasound.so.2', + 'libasound.so.2(ALSA_0.9)', + 'libasound.so.2(ALSA_0.9.0rc4)', + 'libatk-1.0.so.0', + 'libatk-bridge-2.0.so.0', + 'libatspi.so.0', + 'libc.so.6', + 'libc.so.6(GLIBC_2.10)', + 'libc.so.6(GLIBC_2.11)', + 'libc.so.6(GLIBC_2.14)', + 'libc.so.6(GLIBC_2.15)', + 'libc.so.6(GLIBC_2.16)', + 'libc.so.6(GLIBC_2.17)', + 'libc.so.6(GLIBC_2.4)', + 'libc.so.6(GLIBC_2.6)', + 'libc.so.6(GLIBC_2.7)', + 'libc.so.6(GLIBC_2.8)', + 'libc.so.6(GLIBC_2.9)', + 'libcairo.so.2', + 'libcurl.so.4()(64bit)', + 'libdbus-1.so.3', + 'libdl.so.2', + 'libdl.so.2(GLIBC_2.4)', + 'libdrm.so.2', + 'libexpat.so.1', + 'libgbm.so.1', + 'libgcc_s.so.1', + 'libgcc_s.so.1(GCC_3.0)', + 'libgcc_s.so.1(GCC_3.4)', + 'libgcc_s.so.1(GCC_3.5)', + 'libgdk_pixbuf-2.0.so.0', + 'libgio-2.0.so.0', + 'libglib-2.0.so.0', + 'libgmodule-2.0.so.0', + 'libgobject-2.0.so.0', + 'libgtk-3.so.0', + 'libgtk-3.so.0()(64bit)', + 'libm.so.6', + 'libm.so.6(GLIBC_2.4)', + 'libnspr4.so', + 'libnss3.so', + 'libnss3.so(NSS_3.11)', + 'libnss3.so(NSS_3.12)', + 'libnss3.so(NSS_3.12.1)', + 'libnss3.so(NSS_3.2)', + 'libnss3.so(NSS_3.22)', + 'libnss3.so(NSS_3.22)(64bit)', + 'libnss3.so(NSS_3.3)', + 'libnss3.so(NSS_3.4)', + 'libnss3.so(NSS_3.5)', + 'libnss3.so(NSS_3.9.2)', + 'libnssutil3.so', + 'libnssutil3.so(NSSUTIL_3.12.3)', + 'libpango-1.0.so.0', + 'libpthread.so.0', + 'libpthread.so.0(GLIBC_2.12)', + 'libpthread.so.0(GLIBC_2.4)', + 'librt.so.1', + 'librt.so.1(GLIBC_2.4)', + 'libsecret-1.so.0', + 'libsmime3.so', + 'libsmime3.so(NSS_3.10)', + 'libsmime3.so(NSS_3.2)', + 'libssl3.so(NSS_3.28)(64bit)', + 'libstdc++.so.6', + 'libstdc++.so.6(CXXABI_1.3)', + 'libstdc++.so.6(CXXABI_1.3.5)', + 'libstdc++.so.6(CXXABI_1.3.8)', + 'libstdc++.so.6(CXXABI_1.3.9)', + 'libstdc++.so.6(CXXABI_ARM_1.3.3)', + 'libstdc++.so.6(GLIBCXX_3.4)', + 'libstdc++.so.6(GLIBCXX_3.4.11)', + 'libstdc++.so.6(GLIBCXX_3.4.14)', + 'libstdc++.so.6(GLIBCXX_3.4.15)', + 'libstdc++.so.6(GLIBCXX_3.4.18)', + 'libstdc++.so.6(GLIBCXX_3.4.19)', + 'libstdc++.so.6(GLIBCXX_3.4.20)', + 'libstdc++.so.6(GLIBCXX_3.4.21)', + 'libstdc++.so.6(GLIBCXX_3.4.22)', + 'libstdc++.so.6(GLIBCXX_3.4.5)', + 'libstdc++.so.6(GLIBCXX_3.4.9)', + 'libutil.so.1', + 'libutil.so.1(GLIBC_2.4)', + 'libxcb.so.1', + 'libxkbcommon.so.0', + 'libxkbfile.so.1', + 'rpmlib(FileDigests) <= 4.6.0-1', + 'rtld(GNU_HASH)', + 'xdg-utils' + ], + 'aarch64': [ + 'ca-certificates', + 'ld-linux-aarch64.so.1()(64bit)', + 'ld-linux-aarch64.so.1(GLIBC_2.17)(64bit)', + 'libX11.so.6()(64bit)', + 'libXcomposite.so.1()(64bit)', + 'libXdamage.so.1()(64bit)', + 'libXext.so.6()(64bit)', + 'libXfixes.so.3()(64bit)', + 'libXrandr.so.2()(64bit)', + 'libasound.so.2()(64bit)', + 'libasound.so.2(ALSA_0.9)(64bit)', + 'libasound.so.2(ALSA_0.9.0rc4)(64bit)', + 'libatk-1.0.so.0()(64bit)', + 'libatk-bridge-2.0.so.0()(64bit)', + 'libatspi.so.0()(64bit)', + 'libc.so.6()(64bit)', + 'libc.so.6(GLIBC_2.17)(64bit)', + 'libcairo.so.2()(64bit)', + 'libcurl.so.4()(64bit)', + 'libdbus-1.so.3()(64bit)', + 'libdbus-1.so.3(LIBDBUS_1_3)(64bit)', + 'libdl.so.2()(64bit)', + 'libdl.so.2(GLIBC_2.17)(64bit)', + 'libdrm.so.2()(64bit)', + 'libexpat.so.1()(64bit)', + 'libgbm.so.1()(64bit)', + 'libgcc_s.so.1()(64bit)', + 'libgcc_s.so.1(GCC_3.0)(64bit)', + 'libgcc_s.so.1(GCC_4.2.0)(64bit)', + 'libgcc_s.so.1(GCC_4.5.0)(64bit)', + 'libgdk_pixbuf-2.0.so.0()(64bit)', + 'libgio-2.0.so.0()(64bit)', + 'libglib-2.0.so.0()(64bit)', + 'libgmodule-2.0.so.0()(64bit)', + 'libgobject-2.0.so.0()(64bit)', + 'libgtk-3.so.0()(64bit)', + 'libm.so.6()(64bit)', + 'libm.so.6(GLIBC_2.17)(64bit)', + 'libnspr4.so()(64bit)', + 'libnss3.so()(64bit)', + 'libnss3.so(NSS_3.11)(64bit)', + 'libnss3.so(NSS_3.12)(64bit)', + 'libnss3.so(NSS_3.12.1)(64bit)', + 'libnss3.so(NSS_3.2)(64bit)', + 'libnss3.so(NSS_3.22)(64bit)', + 'libnss3.so(NSS_3.3)(64bit)', + 'libnss3.so(NSS_3.4)(64bit)', + 'libnss3.so(NSS_3.5)(64bit)', + 'libnss3.so(NSS_3.9.2)(64bit)', + 'libnssutil3.so()(64bit)', + 'libnssutil3.so(NSSUTIL_3.12.3)(64bit)', + 'libpango-1.0.so.0()(64bit)', + 'libpthread.so.0()(64bit)', + 'libpthread.so.0(GLIBC_2.17)(64bit)', + 'librt.so.1()(64bit)', + 'librt.so.1(GLIBC_2.17)(64bit)', + 'libsecret-1.so.0()(64bit)', + 'libsmime3.so()(64bit)', + 'libsmime3.so(NSS_3.10)(64bit)', + 'libsmime3.so(NSS_3.2)(64bit)', + 'libssl3.so(NSS_3.28)(64bit)', + 'libstdc++.so.6()(64bit)', + 'libstdc++.so.6(CXXABI_1.3)(64bit)', + 'libstdc++.so.6(CXXABI_1.3.5)(64bit)', + 'libstdc++.so.6(CXXABI_1.3.8)(64bit)', + 'libstdc++.so.6(CXXABI_1.3.9)(64bit)', + 'libstdc++.so.6(GLIBCXX_3.4)(64bit)', + 'libstdc++.so.6(GLIBCXX_3.4.11)(64bit)', + 'libstdc++.so.6(GLIBCXX_3.4.14)(64bit)', + 'libstdc++.so.6(GLIBCXX_3.4.15)(64bit)', + 'libstdc++.so.6(GLIBCXX_3.4.18)(64bit)', + 'libstdc++.so.6(GLIBCXX_3.4.19)(64bit)', + 'libstdc++.so.6(GLIBCXX_3.4.20)(64bit)', + 'libstdc++.so.6(GLIBCXX_3.4.21)(64bit)', + 'libstdc++.so.6(GLIBCXX_3.4.22)(64bit)', + 'libstdc++.so.6(GLIBCXX_3.4.5)(64bit)', + 'libstdc++.so.6(GLIBCXX_3.4.9)(64bit)', + 'libutil.so.1()(64bit)', + 'libutil.so.1(GLIBC_2.17)(64bit)', + 'libxcb.so.1()(64bit)', + 'libxkbcommon.so.0()(64bit)', + 'libxkbcommon.so.0(V_0.5.0)(64bit)', + 'libxkbfile.so.1()(64bit)', + 'rpmlib(FileDigests) <= 4.6.0-1', + 'rtld(GNU_HASH)', + 'xdg-utils' + ] +}; diff --git a/build/linux/rpm/dependencies-generator.js b/build/linux/rpm/dependencies-generator.js index ddeefe3c28d..1d91eb8de78 100644 --- a/build/linux/rpm/dependencies-generator.js +++ b/build/linux/rpm/dependencies-generator.js @@ -9,7 +9,15 @@ const child_process_1 = require("child_process"); const fs_1 = require("fs"); const path = require("path"); const dep_lists_1 = require("./dep-lists"); -function getDependencies(buildDir, applicationName) { +// A flag that can easily be toggled. +// Make sure to compile the build directory after toggling the value. +// If false, we warn about new dependencies if they show up +// while running the rpm prepare package task for a release. +// If true, we fail the build if there are new dependencies found during that task. +// The reference dependencies, which one has to update when the new dependencies +// are valid, are in dep-lists.ts +const FAIL_BUILD_FOR_NEW_DEPENDENCIES = true; +function getDependencies(buildDir, applicationName, arch) { // Get the files for which we want to find dependencies. const nativeModulesPath = path.join(buildDir, 'resources', 'app', 'node_modules.asar.unpacked'); const findResult = (0, child_process_1.spawnSync)('find', [nativeModulesPath, '-name', '*.node']); @@ -40,9 +48,22 @@ function getDependencies(buildDir, applicationName) { sortedDependencies = sortedDependencies.filter(dependency => { return !dep_lists_1.bundledDeps.some(bundledDep => dependency.startsWith(bundledDep)); }); + const referenceGeneratedDeps = dep_lists_1.referenceGeneratedDepsByArch[arch]; + if (JSON.stringify(sortedDependencies) !== JSON.stringify(referenceGeneratedDeps)) { + const failMessage = 'The dependencies list has changed. ' + + 'Printing newer dependencies list that one can use to compare against referenceGeneratedDeps:\n' + + sortedDependencies.join('\n'); + if (FAIL_BUILD_FOR_NEW_DEPENDENCIES) { + throw new Error(failMessage); + } + else { + console.warn(failMessage); + } + } return sortedDependencies; } exports.getDependencies = getDependencies; +// Based on https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/rpm/calculate_package_deps.py. function calculatePackageDeps(binaryPath) { try { if (!((0, fs_1.statSync)(binaryPath).mode & fs_1.constants.S_IXUSR)) { @@ -58,9 +79,6 @@ function calculatePackageDeps(binaryPath) { throw new Error(`find-requires failed with exit code ${findRequiresResult.status}.\nstderr: ${findRequiresResult.stderr}`); } const requires = new Set(findRequiresResult.stdout.toString('utf-8').trimEnd().split('\n')); - // we only need to use provides to check for newer dependencies - // const provides = readFileSync('dist_package_provides.json'); - // const jsonProvides = JSON.parse(provides.toString('utf-8')); return requires; } // Based on https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/rpm/merge_package_deps.py diff --git a/build/linux/rpm/dependencies-generator.ts b/build/linux/rpm/dependencies-generator.ts index 97d313fe570..95953ecaa24 100644 --- a/build/linux/rpm/dependencies-generator.ts +++ b/build/linux/rpm/dependencies-generator.ts @@ -8,9 +8,19 @@ import { spawnSync } from 'child_process'; import { constants, statSync } from 'fs'; import path = require('path'); -import { additionalDeps, bundledDeps } from './dep-lists'; +import { additionalDeps, bundledDeps, referenceGeneratedDepsByArch } from './dep-lists'; +import { ArchString } from './types'; -export function getDependencies(buildDir: string, applicationName: string): string[] { +// A flag that can easily be toggled. +// Make sure to compile the build directory after toggling the value. +// If false, we warn about new dependencies if they show up +// while running the rpm prepare package task for a release. +// If true, we fail the build if there are new dependencies found during that task. +// The reference dependencies, which one has to update when the new dependencies +// are valid, are in dep-lists.ts +const FAIL_BUILD_FOR_NEW_DEPENDENCIES: boolean = true; + +export function getDependencies(buildDir: string, applicationName: string, arch: ArchString): string[] { // Get the files for which we want to find dependencies. const nativeModulesPath = path.join(buildDir, 'resources', 'app', 'node_modules.asar.unpacked'); const findResult = spawnSync('find', [nativeModulesPath, '-name', '*.node']); @@ -49,9 +59,22 @@ export function getDependencies(buildDir: string, applicationName: string): stri return !bundledDeps.some(bundledDep => dependency.startsWith(bundledDep)); }); + const referenceGeneratedDeps = referenceGeneratedDepsByArch[arch]; + if (JSON.stringify(sortedDependencies) !== JSON.stringify(referenceGeneratedDeps)) { + const failMessage = 'The dependencies list has changed. ' + + 'Printing newer dependencies list that one can use to compare against referenceGeneratedDeps:\n' + + sortedDependencies.join('\n'); + if (FAIL_BUILD_FOR_NEW_DEPENDENCIES) { + throw new Error(failMessage); + } else { + console.warn(failMessage); + } + } + return sortedDependencies; } +// Based on https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/rpm/calculate_package_deps.py. function calculatePackageDeps(binaryPath: string): Set { try { if (!(statSync(binaryPath).mode & constants.S_IXUSR)) { @@ -68,11 +91,6 @@ function calculatePackageDeps(binaryPath: string): Set { } const requires = new Set(findRequiresResult.stdout.toString('utf-8').trimEnd().split('\n')); - - // we only need to use provides to check for newer dependencies - // const provides = readFileSync('dist_package_provides.json'); - // const jsonProvides = JSON.parse(provides.toString('utf-8')); - return requires; } diff --git a/build/linux/rpm/types.js b/build/linux/rpm/types.js new file mode 100644 index 00000000000..56d4e6c56ce --- /dev/null +++ b/build/linux/rpm/types.js @@ -0,0 +1,6 @@ +"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 }); diff --git a/build/linux/rpm/types.ts b/build/linux/rpm/types.ts new file mode 100644 index 00000000000..84330949d1d --- /dev/null +++ b/build/linux/rpm/types.ts @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export type ArchString = 'x86_64' | 'armv7hl' | 'aarch64'; From ac2a7cb126938feac268e7c143029cabcb5fcdb0 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Tue, 19 Apr 2022 08:33:39 -0700 Subject: [PATCH 143/245] Update Codicons: Update eye icon (#143602) https://github.com/microsoft/vscode-codicons/commit/115660e11af24976db3fb474b0f67067f2d0462b --- .../browser/ui/codicons/codicon/codicon.ttf | Bin 70776 -> 70836 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf index 02beee38fd2f2b644f420ff9cb3fc397b4c82a94..8a5ed7180f8d5b1c9ce3d45e1ae4ce4ced985187 100644 GIT binary patch delta 948 zcmYMue@L2n0LSs~SD$>WmCGN~PJJFtP4l-t+Vg0c({g3%tjt=!=ZB#!@a!-ScdeV&J!{cgA~=Mah!Ky7Gn>g4 zvF+Gi*iP)X?HPN{e%cmpOSa|PPRS;6id-Qdll$a3HBP0ded?XV;z&3)9j}}mr^~tI zeBk`89kkzRf7a37vEoX&vaW+pN9Sbcmb=~^cc-`9mCH+)_dQY%;h{a7o+D4$EBAW5 zS#P0hs_W-&ynCQK+x@~v_-Nm%@5uN5O5K%TdUQSEp69)dz01AFe!_p(pYxyf`THLC zRRZcjAaEnF7T6AQg8txY@JaA>NE*5s`Yv>GHFdQZ9t~&vh5h0F)&AE5tpg9z1KR_? z4(12nMpzL;WGRw~JRhnbdNh?fyD|$4%G`ux@I3gV>j8v}OzV_p&YBV%@ zXY~D;W9-&ge(ZeQGk$OUuL<@L_SemkS_QaHWr>;U%CG7 zvrLkcj3n2d(#i%$S^xz=4%C1G9RToI0vY5$l!Zf*azjeQ5(o)OBqT_?8Co@Jg$%M3 zYCEMsMWl@oig4(L8WRH*nV^)(u<^t-5J6E`BjO4ROz&5JeG)Ds5s5l%!!J17+G8T7(G7KoEiaf{3HVt7Q$!td=Q5E&r3jnntEci69IV zk?5NYX1)XfCa4Z_!3kgkE@%aSB7}kuHd3VcfF|u~4S_?EkOS2k6I8HJ4m3f^&B$EB zFdc?nYT)t2JYGYG)TTr~qHCRj|7kyAS{&D6Jbv{f=DVBpl8-#4(fFSOfTzjH`rN-m C@);!n delta 888 zcmdn8lI6z=mI)3#Hy##TWMEKEVPLrNV4{x+i~8OVViQ}G1zi{z7{3F+L2;jwfzk@4FUkhW8&u3xPN}-7o>NOwTcGZwzDFZWE*J|Kn;1_sUT1vDB*CP@WR}SpQv*{! z(*>qyOn;dP!nuU;smqnMwK8p{Qa+VR6WtO`vA6aEsEwYxe zPP6f{X|ma3D`jhE+hhCA&dsjA$L@iBg#8-(e-0uJE)HD|ha7G>GC3+aHaTu{a&VgG z%;0R~+~mB@Ma0F$CClZI%RQGjuFKqb+zi}SxqWg^azEiA;*sMq%j1HlmS>yi11}CQ z9j^ed0dmrWB8q9Vz;cQVmkKr3IxmrCmv9OLt12k$x*fEyFEiLB@fM zf0|oTSWaQ7^^tkAas#ojmuF;CR#G!H5m(n^)Mo^$6ce4MCC@LZC}$!sq-?WXj-Ovx zN|RebpWlM_VVF4&H@{e{5VJ6kFdL_Y!e22yK{*pS6&qz?89^O!Em>7Q5s7*QepB9a znMNF90z6`ZJc5jZObQYV3=GUb%)kh896y5ugCfvrCU%S Date: Thu, 14 Apr 2022 15:00:00 -0700 Subject: [PATCH 144/245] Don't encode paths --- .../src/languageFeatures/dropIntoEditor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts b/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts index 7e909f874de..5413e53fdc7 100644 --- a/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts +++ b/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts @@ -52,7 +52,7 @@ export function registerDropIntoEditor(selector: vscode.DocumentSelector) { const snippet = new vscode.SnippetString(); uris.forEach((uri, i) => { const mdPath = document.uri.scheme === uri.scheme - ? encodeURIComponent(path.relative(URI.Utils.dirname(document.uri).fsPath, uri.fsPath)) + ? encodeURI(path.relative(URI.Utils.dirname(document.uri).fsPath, uri.fsPath)) : uri.toString(false); const ext = URI.Utils.extname(uri).toLowerCase(); From 56bc85e2df6209499bfde87787d089f244fdab54 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 19 Apr 2022 10:14:39 -0700 Subject: [PATCH 145/245] Add readonly to args --- src/vscode-dts/vscode.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index 0dbe8cb9fc0..c33e8e3de46 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -9437,7 +9437,7 @@ declare module 'vscode' { * * @return New webview panel. */ - export function createWebviewPanel(viewType: string, title: string, showOptions: ViewColumn | { viewColumn: ViewColumn; preserveFocus?: boolean }, options?: WebviewPanelOptions & WebviewOptions): WebviewPanel; + export function createWebviewPanel(viewType: string, title: string, showOptions: ViewColumn | { readonly viewColumn: ViewColumn; readonly preserveFocus?: boolean }, options?: WebviewPanelOptions & WebviewOptions): WebviewPanel; /** * Set a message to the status bar. This is a short hand for the more powerful From 4a6a56a58172e50c51a12efa9832ef191c0ee758 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 19 Apr 2022 10:16:27 -0700 Subject: [PATCH 146/245] Spelling --- src/vscode-dts/vscode.d.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index c33e8e3de46..61f5c32961e 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -1924,12 +1924,12 @@ declare module 'vscode' { title?: string; /** - * The value to prefill in the input box. + * The value to pre-fill in the input box. */ value?: string; /** - * Selection of the prefilled {@linkcode InputBoxOptions.value value}. Defined as tuple of two number where the + * Selection of the pre-filled {@linkcode InputBoxOptions.value value}. Defined as tuple of two number where the * first is the inclusive start index and the second the exclusive end index. When `undefined` the whole * word will be selected, when empty (start equals end) only the cursor will be set, * otherwise the defined range will be selected. @@ -9420,7 +9420,7 @@ declare module 'vscode' { * If language id is not provided, then **Log** is used as default language id. * * You can access the visible or active output channel as a {@link TextDocument text document} from {@link window.visibleTextEditors visible editors} or {@link window.activeTextEditor active editor} - * and use the langage id to contribute language features like syntax coloring, code lens etc., + * and use the langrage id to contribute language features like syntax coloring, code lens etc., * * @param name Human-readable string which will be used to represent the channel in the UI. * @param languageId The identifier of the language associated with the channel. @@ -11504,7 +11504,7 @@ declare module 'vscode' { * vscode.workspace.createFileSystemWatcher('**​/*.js')); * ``` * - * *Note:* the array of workspace folders can be empy if no workspace is opened (empty window). + * *Note:* the array of workspace folders can be empty if no workspace is opened (empty window). * * #### Out of workspace file watching * @@ -11703,7 +11703,7 @@ declare module 'vscode' { * {@linkcode notebook.onDidCloseNotebookDocument onDidCloseNotebookDocument}-event can occur at any time after. * * *Note* that opening a notebook does not show a notebook editor. This function only returns a notebook document which - * can be showns in a notebook editor but it can also be used for other things. + * can be shown in a notebook editor but it can also be used for other things. * * @param uri The resource to open. * @returns A promise that resolves to a {@link NotebookDocument notebook} @@ -11728,7 +11728,7 @@ declare module 'vscode' { * the `onNotebook:` activation event, and extensions must register their serializer in return. * * @param notebookType A notebook. - * @param serializer A notebook serialzier. + * @param serializer A notebook serializer. * @param options Optional context options that define what parts of a notebook should be persisted * @return A {@link Disposable} that unregisters this serializer when being disposed. */ From bb5215fff67fd9f40e247a353cc0e5e84a28f49f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 19 Apr 2022 10:17:08 -0700 Subject: [PATCH 147/245] Pick up new TS version for building VS Code --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 2866cc2fd68..fedd1d6184d 100644 --- a/package.json +++ b/package.json @@ -200,7 +200,7 @@ "style-loader": "^1.0.0", "ts-loader": "^9.2.7", "tsec": "0.1.4", - "typescript": "^4.7.0-dev.20220408", + "typescript": "^4.7.0-dev.20220419", "typescript-formatter": "7.1.0", "underscore": "^1.12.1", "util": "^0.12.4", diff --git a/yarn.lock b/yarn.lock index 9fec7f7d776..e798b48aeae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11404,10 +11404,10 @@ typescript@^2.6.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4" integrity sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q= -typescript@^4.7.0-dev.20220408: - version "4.7.0-dev.20220408" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.0-dev.20220408.tgz#52a049ad6c3c78856935692c3fc69aec8a40ec6d" - integrity sha512-2KgXRQXrjFEbm+ismaGoR803nTRQuc03XQ1crnlxXvQYIXasPiidwiczLc9i0rTo33KXIN2M6YiNtn1tZihHgw== +typescript@^4.7.0-dev.20220419: + version "4.7.0-dev.20220419" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.0-dev.20220419.tgz#c9bc57a9f4e79ee687ac60da6a2170bb3b482467" + integrity sha512-eqezQkTMfRDsbYTW3vWImen8yasP99iykbSLr1BjF7jusxmnvfOVWzXr1zsf+0+FWufBLwpa0rpZBd8TZHWmxg== typical@^4.0.0: version "4.0.0" From dafbfe227f771ab783dbfe3da3fcbd5db76d34b0 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 19 Apr 2022 11:05:03 -0700 Subject: [PATCH 148/245] xterm@4.19.0-beta.22 fixes #147602 fixes #147659 --- package.json | 6 +++--- remote/package.json | 6 +++--- remote/web/package.json | 4 ++-- remote/web/yarn.lock | 16 ++++++++-------- remote/yarn.lock | 24 ++++++++++++------------ yarn.lock | 24 ++++++++++++------------ 6 files changed, 40 insertions(+), 40 deletions(-) diff --git a/package.json b/package.json index fedd1d6184d..9f47e9a99f2 100644 --- a/package.json +++ b/package.json @@ -84,12 +84,12 @@ "vscode-proxy-agent": "^0.12.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.21", - "xterm-addon-search": "0.9.0-beta.20", + "xterm": "4.19.0-beta.22", + "xterm-addon-search": "0.9.0-beta.21", "xterm-addon-serialize": "0.7.0-beta.12", "xterm-addon-unicode11": "0.4.0-beta.3", "xterm-addon-webgl": "0.12.0-beta.27", - "xterm-headless": "4.19.0-beta.21", + "xterm-headless": "4.19.0-beta.22", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/package.json b/remote/package.json index e60a3ddbdbb..be99511dc4a 100644 --- a/remote/package.json +++ b/remote/package.json @@ -24,12 +24,12 @@ "vscode-proxy-agent": "^0.12.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.21", - "xterm-addon-search": "0.9.0-beta.20", + "xterm": "4.19.0-beta.22", + "xterm-addon-search": "0.9.0-beta.21", "xterm-addon-serialize": "0.7.0-beta.12", "xterm-addon-unicode11": "0.4.0-beta.3", "xterm-addon-webgl": "0.12.0-beta.27", - "xterm-headless": "4.19.0-beta.21", + "xterm-headless": "4.19.0-beta.22", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/web/package.json b/remote/web/package.json index 2ef7565d1ab..fea63065f8a 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -10,8 +10,8 @@ "tas-client-umd": "0.1.4", "vscode-oniguruma": "1.6.1", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.21", - "xterm-addon-search": "0.9.0-beta.20", + "xterm": "4.19.0-beta.22", + "xterm-addon-search": "0.9.0-beta.21", "xterm-addon-unicode11": "0.4.0-beta.3", "xterm-addon-webgl": "0.12.0-beta.27" } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 9a8f157bd65..f99540f1546 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -113,10 +113,10 @@ vscode-textmate@7.0.1: resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-7.0.1.tgz#8118a32b02735dccd14f893b495fa5389ad7de79" integrity sha512-zQ5U/nuXAAMsh691FtV0wPz89nSkHbs+IQV8FDk+wew9BlSDhf4UmWGlWJfTR2Ti6xZv87Tj5fENzKf6Qk7aLw== -xterm-addon-search@0.9.0-beta.20: - version "0.9.0-beta.20" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.20.tgz#53e0694d27bebba2b955a0ff58e1fc07670dbfb0" - integrity sha512-kKa43yZIZ6H0tgSoRTgsdLjrml1rGfB/V4fP3sFuEFnboHB0UNBEYssTVW3Mf5Uhxp0waHS/MU0zdnkyRDlC1Q== +xterm-addon-search@0.9.0-beta.21: + version "0.9.0-beta.21" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.21.tgz#5348fe25676cdd89ce3be52ae62a316b6f266176" + integrity sha512-jh6kfRCpWRvZZkV9QghFYesSYHjybaLNEyYAD6uilZYfNHoGLa0zPgUkLOqoECL7K6rhBmSYOkKbc9MG4wNFMQ== xterm-addon-unicode11@0.4.0-beta.3: version "0.4.0-beta.3" @@ -128,7 +128,7 @@ xterm-addon-webgl@0.12.0-beta.27: resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.27.tgz#afc5bc01d1ef3af9005fb9f6325a4db9c92aa8d9" integrity sha512-P948trotU8FMHtaA7C2x97VpLq6QLSjO53kWNvONS0/XwEKQBIYCI7Jfri2wcLgfQg6Cn4OQGLoj2YBK3MMyww== -xterm@4.19.0-beta.21: - version "4.19.0-beta.21" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.21.tgz#bcc603fdb496cae052a1c7c978c0efb531f8a8f3" - integrity sha512-WwtYP0U/NyU6Mw8JIMFHLo3+JUScOugQCqBiFdJHUoeisNEaZSwWV89mSekYLhh6iOgce7Xkq9GRPq1zPcaoYQ== +xterm@4.19.0-beta.22: + version "4.19.0-beta.22" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.22.tgz#628b5a1bf92b205dd3a5dc6e352c80206fc992e8" + integrity sha512-61UqVcuXnXqY33EAPXDzlWo0oQo60O6ssAzkaR04jl67A71ccLnZ0lZSUb7YAVAC9+HhcIVU4lZHAaTzOwsvkA== diff --git a/remote/yarn.lock b/remote/yarn.lock index 9a7f3ce1a4c..875b7870b1a 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -914,10 +914,10 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -xterm-addon-search@0.9.0-beta.20: - version "0.9.0-beta.20" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.20.tgz#53e0694d27bebba2b955a0ff58e1fc07670dbfb0" - integrity sha512-kKa43yZIZ6H0tgSoRTgsdLjrml1rGfB/V4fP3sFuEFnboHB0UNBEYssTVW3Mf5Uhxp0waHS/MU0zdnkyRDlC1Q== +xterm-addon-search@0.9.0-beta.21: + version "0.9.0-beta.21" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.21.tgz#5348fe25676cdd89ce3be52ae62a316b6f266176" + integrity sha512-jh6kfRCpWRvZZkV9QghFYesSYHjybaLNEyYAD6uilZYfNHoGLa0zPgUkLOqoECL7K6rhBmSYOkKbc9MG4wNFMQ== xterm-addon-serialize@0.7.0-beta.12: version "0.7.0-beta.12" @@ -934,15 +934,15 @@ xterm-addon-webgl@0.12.0-beta.27: resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.27.tgz#afc5bc01d1ef3af9005fb9f6325a4db9c92aa8d9" integrity sha512-P948trotU8FMHtaA7C2x97VpLq6QLSjO53kWNvONS0/XwEKQBIYCI7Jfri2wcLgfQg6Cn4OQGLoj2YBK3MMyww== -xterm-headless@4.19.0-beta.21: - version "4.19.0-beta.21" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.21.tgz#903c2221cb57fab0e46b929fa39e71a7ee0525d3" - integrity sha512-zgmwcb/ICIwdOIqhMirHEzhMy3qnqzUgzwl5slFuztbh2ki0GvREBm6iW8EaveqzmaDYzTVwcBa+gE+SyYuwjg== +xterm-headless@4.19.0-beta.22: + version "4.19.0-beta.22" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.22.tgz#23a3ad3f2dcacb43f595dc913afc116a3a60bd45" + integrity sha512-ICueI3qusYo5ozQ9irM1A3vHhWq1VhHvyYK4Qo08m9uVTF3tUU6whklgmXEMSFvZhlSpS4OTW3l4gIa5V4db/Q== -xterm@4.19.0-beta.21: - version "4.19.0-beta.21" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.21.tgz#bcc603fdb496cae052a1c7c978c0efb531f8a8f3" - integrity sha512-WwtYP0U/NyU6Mw8JIMFHLo3+JUScOugQCqBiFdJHUoeisNEaZSwWV89mSekYLhh6iOgce7Xkq9GRPq1zPcaoYQ== +xterm@4.19.0-beta.22: + version "4.19.0-beta.22" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.22.tgz#628b5a1bf92b205dd3a5dc6e352c80206fc992e8" + integrity sha512-61UqVcuXnXqY33EAPXDzlWo0oQo60O6ssAzkaR04jl67A71ccLnZ0lZSUb7YAVAC9+HhcIVU4lZHAaTzOwsvkA== yallist@^4.0.0: version "4.0.0" diff --git a/yarn.lock b/yarn.lock index e798b48aeae..d86cbab08a5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12204,10 +12204,10 @@ xtend@~2.1.1: dependencies: object-keys "~0.4.0" -xterm-addon-search@0.9.0-beta.20: - version "0.9.0-beta.20" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.20.tgz#53e0694d27bebba2b955a0ff58e1fc07670dbfb0" - integrity sha512-kKa43yZIZ6H0tgSoRTgsdLjrml1rGfB/V4fP3sFuEFnboHB0UNBEYssTVW3Mf5Uhxp0waHS/MU0zdnkyRDlC1Q== +xterm-addon-search@0.9.0-beta.21: + version "0.9.0-beta.21" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.21.tgz#5348fe25676cdd89ce3be52ae62a316b6f266176" + integrity sha512-jh6kfRCpWRvZZkV9QghFYesSYHjybaLNEyYAD6uilZYfNHoGLa0zPgUkLOqoECL7K6rhBmSYOkKbc9MG4wNFMQ== xterm-addon-serialize@0.7.0-beta.12: version "0.7.0-beta.12" @@ -12224,15 +12224,15 @@ xterm-addon-webgl@0.12.0-beta.27: resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.27.tgz#afc5bc01d1ef3af9005fb9f6325a4db9c92aa8d9" integrity sha512-P948trotU8FMHtaA7C2x97VpLq6QLSjO53kWNvONS0/XwEKQBIYCI7Jfri2wcLgfQg6Cn4OQGLoj2YBK3MMyww== -xterm-headless@4.19.0-beta.21: - version "4.19.0-beta.21" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.21.tgz#903c2221cb57fab0e46b929fa39e71a7ee0525d3" - integrity sha512-zgmwcb/ICIwdOIqhMirHEzhMy3qnqzUgzwl5slFuztbh2ki0GvREBm6iW8EaveqzmaDYzTVwcBa+gE+SyYuwjg== +xterm-headless@4.19.0-beta.22: + version "4.19.0-beta.22" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.22.tgz#23a3ad3f2dcacb43f595dc913afc116a3a60bd45" + integrity sha512-ICueI3qusYo5ozQ9irM1A3vHhWq1VhHvyYK4Qo08m9uVTF3tUU6whklgmXEMSFvZhlSpS4OTW3l4gIa5V4db/Q== -xterm@4.19.0-beta.21: - version "4.19.0-beta.21" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.21.tgz#bcc603fdb496cae052a1c7c978c0efb531f8a8f3" - integrity sha512-WwtYP0U/NyU6Mw8JIMFHLo3+JUScOugQCqBiFdJHUoeisNEaZSwWV89mSekYLhh6iOgce7Xkq9GRPq1zPcaoYQ== +xterm@4.19.0-beta.22: + version "4.19.0-beta.22" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.22.tgz#628b5a1bf92b205dd3a5dc6e352c80206fc992e8" + integrity sha512-61UqVcuXnXqY33EAPXDzlWo0oQo60O6ssAzkaR04jl67A71ccLnZ0lZSUb7YAVAC9+HhcIVU4lZHAaTzOwsvkA== y18n@^3.2.1: version "3.2.2" From d2e01929e38aeb76e0163c9c818511307aeb01ae Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 19 Apr 2022 11:52:59 -0700 Subject: [PATCH 149/245] fix #147717 --- src/vs/platform/terminal/node/terminalEnvironment.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/terminal/node/terminalEnvironment.ts b/src/vs/platform/terminal/node/terminalEnvironment.ts index 78009d3c17e..84c0f73c9a0 100644 --- a/src/vs/platform/terminal/node/terminalEnvironment.ts +++ b/src/vs/platform/terminal/node/terminalEnvironment.ts @@ -108,7 +108,7 @@ export function getShellIntegrationInjection( // - The global setting is disabled // - There is no executable (not sure what script to run) // - The terminal is used by a feature like tasks or debugging - if (!options.enabled || !shellLaunchConfig.executable || shellLaunchConfig.isFeatureTerminal) { + if (!options.enabled || !shellLaunchConfig.executable || shellLaunchConfig.isFeatureTerminal || shellLaunchConfig.hideFromUser) { return undefined; } From 800eec5645cf45d3cae5f49d4fc18e76e0e028ab Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 19 Apr 2022 13:07:42 -0700 Subject: [PATCH 150/245] Mark setting as experimental again Will finalize along when we actually ship this api --- extensions/markdown-language-features/package.nls.json | 2 +- src/vs/workbench/browser/parts/editor/editorDropTarget.ts | 2 +- src/vs/workbench/browser/workbench.contribution.ts | 3 ++- src/vscode-dts/vscode.proposed.textEditorDrop.d.ts | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/extensions/markdown-language-features/package.nls.json b/extensions/markdown-language-features/package.nls.json index 444c112e9de..ab5e295770b 100644 --- a/extensions/markdown-language-features/package.nls.json +++ b/extensions/markdown-language-features/package.nls.json @@ -28,6 +28,6 @@ "configuration.markdown.links.openLocation.currentGroup": "Open links in the active editor group.", "configuration.markdown.links.openLocation.beside": "Open links beside the active editor.", "configuration.markdown.suggest.paths.enabled.description": "Enable/disable path suggestions for markdown links", - "configuration.markdown.editor.drop.enabled": "Enable/disable dropping into the markdown editor to insert shift. Requires enabling `#workbench.editor.dropIntoEditor.enabled#`.", + "configuration.markdown.editor.drop.enabled": "Enable/disable dropping into the markdown editor to insert shift. Requires enabling `#workbenck.experimental.editor.dropIntoEditor.enabled#`.", "workspaceTrust": "Required for loading styles configured in the workspace." } diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index 5dbfdc1a16a..8e91c71f640 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -31,7 +31,7 @@ interface IDropOperation { } function isDropIntoEditorEnabledGlobally(configurationService: IConfigurationService) { - return configurationService.getValue('workbench.editor.dropIntoEditor.enabled'); + return configurationService.getValue('workbench.experimental.editor.dropIntoEditor.enabled'); } function isDragIntoEditorEvent(e: DragEvent): boolean { diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 46d04a0b0ec..77d6942b08a 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -464,9 +464,10 @@ const registry = Registry.as(ConfigurationExtensions.Con 'description': localize('layoutControlType', "Controls whether the layout control in the custom title bar is displayed as a single menu button or with multiple UI toggles."), 'markdownDeprecationMessage': localize({ key: 'layoutControlTypeDeprecation', comment: ['{0} is a placeholder for a setting identifier.'] }, "This setting has been deprecated in favor of {0}", '`#workbench.layoutControl.type#`') }, - 'workbench.editor.dropIntoEditor.enabled': { + 'workbench.experimental.editor.dropIntoEditor.enabled': { 'type': 'boolean', 'default': true, + 'tags': ['experimental'], 'markdownDescription': localize('dropIntoEditor', "Controls whether you can drag and drop a file into a text editor by holding down `shift` (instead of opening the file in an editor)."), } } diff --git a/src/vscode-dts/vscode.proposed.textEditorDrop.d.ts b/src/vscode-dts/vscode.proposed.textEditorDrop.d.ts index f0ea3649a82..879e5a757b7 100644 --- a/src/vscode-dts/vscode.proposed.textEditorDrop.d.ts +++ b/src/vscode-dts/vscode.proposed.textEditorDrop.d.ts @@ -16,7 +16,7 @@ declare module 'vscode' { /** * Provider which handles dropping of resources into a text editor. * - * The user can drop into a text editor by holding down `shift` while dragging. Requires `workbench.editor.dropIntoEditor.enabled` to be on. + * The user can drop into a text editor by holding down `shift` while dragging. Requires `workbench.experimental.editor.dropIntoEditor.enabled` to be on. */ export interface DocumentOnDropProvider { /** From 94de38e7ddf14390f551b53c11f6d7d934b48b9c Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 19 Apr 2022 22:33:29 +0200 Subject: [PATCH 151/245] Provide emmet abbreviations via inline completions (#147387) This PR changes Emmet to use inline completions when suggesting completions. It also provides a boolean setting so that users can opt out. This PR does not change the "Wrap with Abbreviation" behaviour in Emmet. Co-authored-by: Raymond Zhao --- extensions/emmet/package.json | 8 ++++ extensions/emmet/package.nls.json | 3 +- extensions/emmet/src/emmetCommon.ts | 72 ++++++++++++++++++++++++----- extensions/emmet/tsconfig.json | 3 +- 4 files changed, 73 insertions(+), 13 deletions(-) diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index 659be1e0fce..c256b4fa64f 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -16,6 +16,9 @@ "type": "git", "url": "https://github.com/microsoft/vscode.git" }, + "enabledApiProposals": [ + "inlineCompletionsNew" + ], "activationEvents": [ "onCommand:emmet.expandAbbreviation", "onCommand:editor.emmet.action.wrapWithAbbreviation", @@ -127,6 +130,11 @@ "default": false, "markdownDescription": "%emmetTriggerExpansionOnTab%" }, + "emmet.useInlineCompletions": { + "type": "boolean", + "default": true, + "markdownDescription": "%emmetUseInlineCompletions%" + }, "emmet.preferences": { "type": "object", "default": {}, diff --git a/extensions/emmet/package.nls.json b/extensions/emmet/package.nls.json index 1cac53a6081..3de8f3205c1 100644 --- a/extensions/emmet/package.nls.json +++ b/extensions/emmet/package.nls.json @@ -59,5 +59,6 @@ "emmetPreferencesOutputInlineBreak": "The number of sibling inline elements needed for line breaks to be placed between those elements. If `0`, inline elements are always expanded onto a single line.", "emmetPreferencesOutputReverseAttributes": "If `true`, reverses attribute merging directions when resolving snippets.", "emmetPreferencesOutputSelfClosingStyle": "Style of self-closing tags: html (`
`), xml (`
`) or xhtml (`
`).", - "emmetPreferencesCssColorShort": "If `true`, color values like `#f` will be expanded to `#fff` instead of `#ffffff`." + "emmetPreferencesCssColorShort": "If `true`, color values like `#f` will be expanded to `#fff` instead of `#ffffff`.", + "emmetUseInlineCompletions": "If `true`, Emmet will use inline completions to suggest expansions." } diff --git a/extensions/emmet/src/emmetCommon.ts b/extensions/emmet/src/emmetCommon.ts index 5a5e0d872c3..82a32ac9b4e 100644 --- a/extensions/emmet/src/emmetCommon.ts +++ b/extensions/emmet/src/emmetCommon.ts @@ -23,7 +23,7 @@ import { addFileToParseCache, clearParseCache, removeFileFromParseCache } from ' export function activateEmmetExtension(context: vscode.ExtensionContext) { migrateEmmetExtensionsPath(); - registerCompletionProviders(context); + refreshCompletionProviders(context); updateEmmetExtensionsPath(); context.subscriptions.push(vscode.commands.registerCommand('editor.emmet.action.wrapWithAbbreviation', (args) => { @@ -122,8 +122,8 @@ export function activateEmmetExtension(context: vscode.ExtensionContext) { })); context.subscriptions.push(vscode.workspace.onDidChangeConfiguration((e) => { - if (e.affectsConfiguration('emmet.includeLanguages')) { - registerCompletionProviders(context); + if (e.affectsConfiguration('emmet.includeLanguages') || e.affectsConfiguration('emmet.useInlineCompletions')) { + refreshCompletionProviders(context); } if (e.affectsConfiguration('emmet.extensionsPath')) { updateEmmetExtensionsPath(); @@ -159,11 +159,42 @@ export function activateEmmetExtension(context: vscode.ExtensionContext) { */ const languageMappingForCompletionProviders: Map = new Map(); const completionProvidersMapping: Map = new Map(); +const completionProviderDisposables: vscode.Disposable[] = []; -function registerCompletionProviders(context: vscode.ExtensionContext) { - let completionProvider = new DefaultCompletionItemProvider(); - let includedLanguages = getMappingForIncludedLanguages(); +function refreshCompletionProviders(_: vscode.ExtensionContext) { + clearCompletionProviderInfo(); + const completionProvider = new DefaultCompletionItemProvider(); + const inlineCompletionProvider: vscode.InlineCompletionItemProviderNew = { + async provideInlineCompletionItems(document: vscode.TextDocument, position: vscode.Position, _: vscode.InlineCompletionContextNew, token: vscode.CancellationToken) { + const items = await completionProvider.provideCompletionItems(document, position, token, { triggerCharacter: undefined, triggerKind: vscode.CompletionTriggerKind.Invoke }); + if (!items) { + return undefined; + } + const item = items.items[0]; + if (!item) { + return undefined; + } + const range = item.range as vscode.Range; + + if (document.getText(range) !== item.label) { + // We only want to show an inline completion if we are really sure the user meant emmet. + // If the user types `d`, we don't want to suggest `
`. + return undefined; + } + + return [ + { + insertText: item.insertText as any, + filterText: item.label as any, + range + } + ]; + } + }; + + const useInlineCompletionProvider = vscode.workspace.getConfiguration('emmet').get('useInlineCompletions'); + const includedLanguages = getMappingForIncludedLanguages(); Object.keys(includedLanguages).forEach(language => { if (languageMappingForCompletionProviders.has(language) && languageMappingForCompletionProviders.get(language) === includedLanguages[language]) { return; @@ -178,8 +209,13 @@ function registerCompletionProviders(context: vscode.ExtensionContext) { completionProvidersMapping.delete(language); } - const provider = vscode.languages.registerCompletionItemProvider({ language, scheme: '*' }, completionProvider, ...LANGUAGE_MODES[includedLanguages[language]]); - context.subscriptions.push(provider); + let provider; + if (useInlineCompletionProvider) { + provider = vscode.languages.registerInlineCompletionItemProviderNew({ language, scheme: '*' }, inlineCompletionProvider); + } else { + provider = vscode.languages.registerCompletionItemProvider({ language, scheme: '*' }, completionProvider, ...LANGUAGE_MODES[includedLanguages[language]]); + } + completionProviderDisposables.push(provider); languageMappingForCompletionProviders.set(language, includedLanguages[language]); completionProvidersMapping.set(language, provider); @@ -187,8 +223,13 @@ function registerCompletionProviders(context: vscode.ExtensionContext) { Object.keys(LANGUAGE_MODES).forEach(language => { if (!languageMappingForCompletionProviders.has(language)) { - const provider = vscode.languages.registerCompletionItemProvider({ language, scheme: '*' }, completionProvider, ...LANGUAGE_MODES[language]); - context.subscriptions.push(provider); + let provider; + if (useInlineCompletionProvider) { + provider = vscode.languages.registerInlineCompletionItemProviderNew({ language, scheme: '*' }, inlineCompletionProvider); + } else { + provider = vscode.languages.registerCompletionItemProvider({ language, scheme: '*' }, completionProvider, ...LANGUAGE_MODES[language]); + } + completionProviderDisposables.push(provider); languageMappingForCompletionProviders.set(language, language); completionProvidersMapping.set(language, provider); @@ -196,7 +237,16 @@ function registerCompletionProviders(context: vscode.ExtensionContext) { }); } -export function deactivate() { +function clearCompletionProviderInfo() { + languageMappingForCompletionProviders.clear(); completionProvidersMapping.clear(); + let disposable: vscode.Disposable | undefined; + while (disposable = completionProviderDisposables.pop()) { + disposable.dispose(); + } +} + +export function deactivate() { + clearCompletionProviderInfo(); clearParseCache(); } diff --git a/extensions/emmet/tsconfig.json b/extensions/emmet/tsconfig.json index 994fa239537..ad07467f4ea 100644 --- a/extensions/emmet/tsconfig.json +++ b/extensions/emmet/tsconfig.json @@ -9,6 +9,7 @@ ], "include": [ "src/**/*", - "../../src/vscode-dts/vscode.d.ts" + "../../src/vscode-dts/vscode.d.ts", + "../../src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts", ] } From 8ee64bad04da50ed94a572ee51b32adccaec12a1 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Tue, 19 Apr 2022 14:53:07 -0700 Subject: [PATCH 152/245] added docs. Fixes #145673 --- .../vscode.proposed.inputBoxSeverity.d.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/vscode-dts/vscode.proposed.inputBoxSeverity.d.ts b/src/vscode-dts/vscode.proposed.inputBoxSeverity.d.ts index 292abaa8f86..66b2da1b68b 100644 --- a/src/vscode-dts/vscode.proposed.inputBoxSeverity.d.ts +++ b/src/vscode-dts/vscode.proposed.inputBoxSeverity.d.ts @@ -7,14 +7,29 @@ declare module 'vscode' { // https://github.com/microsoft/vscode/issues/144944 + /** + * Impacts the behavior and appearance of the validation message. + */ export enum InputBoxValidationSeverity { Info = 1, Warning = 2, Error = 3 } + /** + * Object to configure the behavior of the validation message. + */ export interface InputBoxValidationMessage { + /** + * The validation message to display. + */ readonly message: string; + + /** + * The severity of the validation message. + * NOTE: When using `InputBoxValidationSeverity.Error`, the user will not be allowed to accept (hit ENTER) the input. + * `Info` and `Warning` will still allow the InputBox to accept the input. + */ readonly severity: InputBoxValidationSeverity; } From 2078613c470c8041f667f4e4b972bac7f333b50a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 19 Apr 2022 15:13:19 -0700 Subject: [PATCH 153/245] =?UTF-8?q?Use=20=E2=87=A7=20for=20shift=20key=20l?= =?UTF-8?q?abel=20on=20mac?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/vs/workbench/browser/parts/editor/editorDropTarget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index 8e91c71f640..0ee0ad2f093 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -101,7 +101,7 @@ class DropOverlay extends Themable { container.appendChild(this.overlay); if (this.enableDropIntoEditor) { - this.dropIntoPromptElement = renderFormattedText(localize('dropIntoEditorPrompt', "Hold __shift__ to drop into editor"), {}); + this.dropIntoPromptElement = renderFormattedText(localize('dropIntoEditorPrompt', "Hold __{0}__ to drop into editor", isMacintosh ? '⇧' : 'Shift'), {}); this.dropIntoPromptElement.classList.add('editor-group-overlay-drop-into-prompt'); this.overlay.appendChild(this.dropIntoPromptElement); } From 5ff2eb2b909ab2711ccd76291021edba0b612415 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 19 Apr 2022 15:41:14 -0700 Subject: [PATCH 154/245] xterm@4.19.0-beta.23 fixes #146406 --- 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 9f47e9a99f2..550e28ce0b7 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "vscode-proxy-agent": "^0.12.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.22", + "xterm": "4.19.0-beta.23", "xterm-addon-search": "0.9.0-beta.21", "xterm-addon-serialize": "0.7.0-beta.12", "xterm-addon-unicode11": "0.4.0-beta.3", diff --git a/remote/package.json b/remote/package.json index be99511dc4a..548e42fe932 100644 --- a/remote/package.json +++ b/remote/package.json @@ -24,7 +24,7 @@ "vscode-proxy-agent": "^0.12.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.22", + "xterm": "4.19.0-beta.23", "xterm-addon-search": "0.9.0-beta.21", "xterm-addon-serialize": "0.7.0-beta.12", "xterm-addon-unicode11": "0.4.0-beta.3", diff --git a/remote/web/package.json b/remote/web/package.json index fea63065f8a..5c1648f1633 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -10,7 +10,7 @@ "tas-client-umd": "0.1.4", "vscode-oniguruma": "1.6.1", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.22", + "xterm": "4.19.0-beta.23", "xterm-addon-search": "0.9.0-beta.21", "xterm-addon-unicode11": "0.4.0-beta.3", "xterm-addon-webgl": "0.12.0-beta.27" diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index f99540f1546..05f9a318136 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -128,7 +128,7 @@ xterm-addon-webgl@0.12.0-beta.27: resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.27.tgz#afc5bc01d1ef3af9005fb9f6325a4db9c92aa8d9" integrity sha512-P948trotU8FMHtaA7C2x97VpLq6QLSjO53kWNvONS0/XwEKQBIYCI7Jfri2wcLgfQg6Cn4OQGLoj2YBK3MMyww== -xterm@4.19.0-beta.22: - version "4.19.0-beta.22" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.22.tgz#628b5a1bf92b205dd3a5dc6e352c80206fc992e8" - integrity sha512-61UqVcuXnXqY33EAPXDzlWo0oQo60O6ssAzkaR04jl67A71ccLnZ0lZSUb7YAVAC9+HhcIVU4lZHAaTzOwsvkA== +xterm@4.19.0-beta.23: + version "4.19.0-beta.23" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.23.tgz#d5a154530c0f7ea05998efad5c9937f4a111ff57" + integrity sha512-WA0thNR2r1OtEQ0qCwxcfL8PZ6EdWWXHbgIp/3PMhWoxVKdBmg5nntJGnmFB5ZVnp5OlQbAymcgFlG8KnNzwag== diff --git a/remote/yarn.lock b/remote/yarn.lock index 875b7870b1a..af39af365da 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -939,10 +939,10 @@ xterm-headless@4.19.0-beta.22: resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.22.tgz#23a3ad3f2dcacb43f595dc913afc116a3a60bd45" integrity sha512-ICueI3qusYo5ozQ9irM1A3vHhWq1VhHvyYK4Qo08m9uVTF3tUU6whklgmXEMSFvZhlSpS4OTW3l4gIa5V4db/Q== -xterm@4.19.0-beta.22: - version "4.19.0-beta.22" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.22.tgz#628b5a1bf92b205dd3a5dc6e352c80206fc992e8" - integrity sha512-61UqVcuXnXqY33EAPXDzlWo0oQo60O6ssAzkaR04jl67A71ccLnZ0lZSUb7YAVAC9+HhcIVU4lZHAaTzOwsvkA== +xterm@4.19.0-beta.23: + version "4.19.0-beta.23" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.23.tgz#d5a154530c0f7ea05998efad5c9937f4a111ff57" + integrity sha512-WA0thNR2r1OtEQ0qCwxcfL8PZ6EdWWXHbgIp/3PMhWoxVKdBmg5nntJGnmFB5ZVnp5OlQbAymcgFlG8KnNzwag== yallist@^4.0.0: version "4.0.0" diff --git a/yarn.lock b/yarn.lock index d86cbab08a5..375d6d3be40 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12229,10 +12229,10 @@ xterm-headless@4.19.0-beta.22: resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.22.tgz#23a3ad3f2dcacb43f595dc913afc116a3a60bd45" integrity sha512-ICueI3qusYo5ozQ9irM1A3vHhWq1VhHvyYK4Qo08m9uVTF3tUU6whklgmXEMSFvZhlSpS4OTW3l4gIa5V4db/Q== -xterm@4.19.0-beta.22: - version "4.19.0-beta.22" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.22.tgz#628b5a1bf92b205dd3a5dc6e352c80206fc992e8" - integrity sha512-61UqVcuXnXqY33EAPXDzlWo0oQo60O6ssAzkaR04jl67A71ccLnZ0lZSUb7YAVAC9+HhcIVU4lZHAaTzOwsvkA== +xterm@4.19.0-beta.23: + version "4.19.0-beta.23" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.23.tgz#d5a154530c0f7ea05998efad5c9937f4a111ff57" + integrity sha512-WA0thNR2r1OtEQ0qCwxcfL8PZ6EdWWXHbgIp/3PMhWoxVKdBmg5nntJGnmFB5ZVnp5OlQbAymcgFlG8KnNzwag== y18n@^3.2.1: version "3.2.2" From 08f9528d33e01abaa21ff558330a3c6d4d268185 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 19 Apr 2022 16:03:44 -0700 Subject: [PATCH 155/245] Add 'notebookCellResource' context key. Fix #146686 --- .../browser/view/cellParts/cellContextKeys.ts | 11 +++++++---- .../contrib/notebook/common/notebookContextKeys.ts | 2 ++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys.ts index e1e2a982be5..364939e3456 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys.ts @@ -5,15 +5,15 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { CellEditState, CellFocusMode, ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { NotebookCellExecutionStateContext, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_FOCUSED, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_LINE_NUMBERS, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_TYPE } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; +import { CellViewModelStateChangeEvent } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; +import { CellPart } from 'vs/workbench/contrib/notebook/browser/view/cellPart'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; import { NotebookCellExecutionState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { NotebookCellExecutionStateContext, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_CELL_EXECUTING, NOTEBOOK_CELL_EXECUTION_STATE, NOTEBOOK_CELL_FOCUSED, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_INPUT_COLLAPSED, NOTEBOOK_CELL_LINE_NUMBERS, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_OUTPUT_COLLAPSED, NOTEBOOK_CELL_RESOURCE, NOTEBOOK_CELL_TYPE } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; -import { CellViewModelStateChangeEvent } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; -import { CellPart } from 'vs/workbench/contrib/notebook/browser/view/cellPart'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; export class CellContextKeyPart extends CellPart { private cellContextKeyManager: CellContextKeyManager; @@ -44,6 +44,7 @@ export class CellContextKeyManager extends Disposable { private cellContentCollapsed!: IContextKey; private cellOutputCollapsed!: IContextKey; private cellLineNumbers!: IContextKey<'on' | 'off' | 'inherit'>; + private cellResource!: IContextKey; private markdownEditMode!: IContextKey; @@ -69,6 +70,7 @@ export class CellContextKeyManager extends Disposable { this.cellContentCollapsed = NOTEBOOK_CELL_INPUT_COLLAPSED.bindTo(this._contextKeyService); this.cellOutputCollapsed = NOTEBOOK_CELL_OUTPUT_COLLAPSED.bindTo(this._contextKeyService); this.cellLineNumbers = NOTEBOOK_CELL_LINE_NUMBERS.bindTo(this._contextKeyService); + this.cellResource = NOTEBOOK_CELL_RESOURCE.bindTo(this._contextKeyService); if (element) { this.updateForElement(element); @@ -112,6 +114,7 @@ export class CellContextKeyManager extends Disposable { this.updateForOutputs(); this.cellLineNumbers.set(this.element!.lineNumbers); + this.cellResource.set(this.element!.uri.toString()); }); } diff --git a/src/vs/workbench/contrib/notebook/common/notebookContextKeys.ts b/src/vs/workbench/contrib/notebook/common/notebookContextKeys.ts index fd2279cba63..689f9ca995e 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookContextKeys.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookContextKeys.ts @@ -38,6 +38,8 @@ export const NOTEBOOK_CELL_EXECUTING = new RawContextKey('notebookCellE export const NOTEBOOK_CELL_HAS_OUTPUTS = new RawContextKey('notebookCellHasOutputs', false); export const NOTEBOOK_CELL_INPUT_COLLAPSED = new RawContextKey('notebookCellInputIsCollapsed', false); export const NOTEBOOK_CELL_OUTPUT_COLLAPSED = new RawContextKey('notebookCellOutputIsCollapsed', false); +export const NOTEBOOK_CELL_RESOURCE = new RawContextKey('notebookCellResource', ''); + // Kernels export const NOTEBOOK_KERNEL = new RawContextKey('notebookKernel', undefined); export const NOTEBOOK_KERNEL_COUNT = new RawContextKey('notebookKernelCount', 0); From 98db85599ea5b78556991f5d1c4646ad270545b2 Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Tue, 19 Apr 2022 16:31:57 -0700 Subject: [PATCH 156/245] Revert "Provide emmet abbreviations via inline completions (#147387)" This reverts commit 94de38e7ddf14390f551b53c11f6d7d934b48b9c. Reason: Found some bugs while demoing the feature. --- extensions/emmet/package.json | 8 ---- extensions/emmet/package.nls.json | 3 +- extensions/emmet/src/emmetCommon.ts | 72 +++++------------------------ extensions/emmet/tsconfig.json | 3 +- 4 files changed, 13 insertions(+), 73 deletions(-) diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index c256b4fa64f..659be1e0fce 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -16,9 +16,6 @@ "type": "git", "url": "https://github.com/microsoft/vscode.git" }, - "enabledApiProposals": [ - "inlineCompletionsNew" - ], "activationEvents": [ "onCommand:emmet.expandAbbreviation", "onCommand:editor.emmet.action.wrapWithAbbreviation", @@ -130,11 +127,6 @@ "default": false, "markdownDescription": "%emmetTriggerExpansionOnTab%" }, - "emmet.useInlineCompletions": { - "type": "boolean", - "default": true, - "markdownDescription": "%emmetUseInlineCompletions%" - }, "emmet.preferences": { "type": "object", "default": {}, diff --git a/extensions/emmet/package.nls.json b/extensions/emmet/package.nls.json index 3de8f3205c1..1cac53a6081 100644 --- a/extensions/emmet/package.nls.json +++ b/extensions/emmet/package.nls.json @@ -59,6 +59,5 @@ "emmetPreferencesOutputInlineBreak": "The number of sibling inline elements needed for line breaks to be placed between those elements. If `0`, inline elements are always expanded onto a single line.", "emmetPreferencesOutputReverseAttributes": "If `true`, reverses attribute merging directions when resolving snippets.", "emmetPreferencesOutputSelfClosingStyle": "Style of self-closing tags: html (`
`), xml (`
`) or xhtml (`
`).", - "emmetPreferencesCssColorShort": "If `true`, color values like `#f` will be expanded to `#fff` instead of `#ffffff`.", - "emmetUseInlineCompletions": "If `true`, Emmet will use inline completions to suggest expansions." + "emmetPreferencesCssColorShort": "If `true`, color values like `#f` will be expanded to `#fff` instead of `#ffffff`." } diff --git a/extensions/emmet/src/emmetCommon.ts b/extensions/emmet/src/emmetCommon.ts index 82a32ac9b4e..5a5e0d872c3 100644 --- a/extensions/emmet/src/emmetCommon.ts +++ b/extensions/emmet/src/emmetCommon.ts @@ -23,7 +23,7 @@ import { addFileToParseCache, clearParseCache, removeFileFromParseCache } from ' export function activateEmmetExtension(context: vscode.ExtensionContext) { migrateEmmetExtensionsPath(); - refreshCompletionProviders(context); + registerCompletionProviders(context); updateEmmetExtensionsPath(); context.subscriptions.push(vscode.commands.registerCommand('editor.emmet.action.wrapWithAbbreviation', (args) => { @@ -122,8 +122,8 @@ export function activateEmmetExtension(context: vscode.ExtensionContext) { })); context.subscriptions.push(vscode.workspace.onDidChangeConfiguration((e) => { - if (e.affectsConfiguration('emmet.includeLanguages') || e.affectsConfiguration('emmet.useInlineCompletions')) { - refreshCompletionProviders(context); + if (e.affectsConfiguration('emmet.includeLanguages')) { + registerCompletionProviders(context); } if (e.affectsConfiguration('emmet.extensionsPath')) { updateEmmetExtensionsPath(); @@ -159,42 +159,11 @@ export function activateEmmetExtension(context: vscode.ExtensionContext) { */ const languageMappingForCompletionProviders: Map = new Map(); const completionProvidersMapping: Map = new Map(); -const completionProviderDisposables: vscode.Disposable[] = []; -function refreshCompletionProviders(_: vscode.ExtensionContext) { - clearCompletionProviderInfo(); +function registerCompletionProviders(context: vscode.ExtensionContext) { + let completionProvider = new DefaultCompletionItemProvider(); + let includedLanguages = getMappingForIncludedLanguages(); - const completionProvider = new DefaultCompletionItemProvider(); - const inlineCompletionProvider: vscode.InlineCompletionItemProviderNew = { - async provideInlineCompletionItems(document: vscode.TextDocument, position: vscode.Position, _: vscode.InlineCompletionContextNew, token: vscode.CancellationToken) { - const items = await completionProvider.provideCompletionItems(document, position, token, { triggerCharacter: undefined, triggerKind: vscode.CompletionTriggerKind.Invoke }); - if (!items) { - return undefined; - } - const item = items.items[0]; - if (!item) { - return undefined; - } - const range = item.range as vscode.Range; - - if (document.getText(range) !== item.label) { - // We only want to show an inline completion if we are really sure the user meant emmet. - // If the user types `d`, we don't want to suggest `
`. - return undefined; - } - - return [ - { - insertText: item.insertText as any, - filterText: item.label as any, - range - } - ]; - } - }; - - const useInlineCompletionProvider = vscode.workspace.getConfiguration('emmet').get('useInlineCompletions'); - const includedLanguages = getMappingForIncludedLanguages(); Object.keys(includedLanguages).forEach(language => { if (languageMappingForCompletionProviders.has(language) && languageMappingForCompletionProviders.get(language) === includedLanguages[language]) { return; @@ -209,13 +178,8 @@ function refreshCompletionProviders(_: vscode.ExtensionContext) { completionProvidersMapping.delete(language); } - let provider; - if (useInlineCompletionProvider) { - provider = vscode.languages.registerInlineCompletionItemProviderNew({ language, scheme: '*' }, inlineCompletionProvider); - } else { - provider = vscode.languages.registerCompletionItemProvider({ language, scheme: '*' }, completionProvider, ...LANGUAGE_MODES[includedLanguages[language]]); - } - completionProviderDisposables.push(provider); + const provider = vscode.languages.registerCompletionItemProvider({ language, scheme: '*' }, completionProvider, ...LANGUAGE_MODES[includedLanguages[language]]); + context.subscriptions.push(provider); languageMappingForCompletionProviders.set(language, includedLanguages[language]); completionProvidersMapping.set(language, provider); @@ -223,13 +187,8 @@ function refreshCompletionProviders(_: vscode.ExtensionContext) { Object.keys(LANGUAGE_MODES).forEach(language => { if (!languageMappingForCompletionProviders.has(language)) { - let provider; - if (useInlineCompletionProvider) { - provider = vscode.languages.registerInlineCompletionItemProviderNew({ language, scheme: '*' }, inlineCompletionProvider); - } else { - provider = vscode.languages.registerCompletionItemProvider({ language, scheme: '*' }, completionProvider, ...LANGUAGE_MODES[language]); - } - completionProviderDisposables.push(provider); + const provider = vscode.languages.registerCompletionItemProvider({ language, scheme: '*' }, completionProvider, ...LANGUAGE_MODES[language]); + context.subscriptions.push(provider); languageMappingForCompletionProviders.set(language, language); completionProvidersMapping.set(language, provider); @@ -237,16 +196,7 @@ function refreshCompletionProviders(_: vscode.ExtensionContext) { }); } -function clearCompletionProviderInfo() { - languageMappingForCompletionProviders.clear(); - completionProvidersMapping.clear(); - let disposable: vscode.Disposable | undefined; - while (disposable = completionProviderDisposables.pop()) { - disposable.dispose(); - } -} - export function deactivate() { - clearCompletionProviderInfo(); + completionProvidersMapping.clear(); clearParseCache(); } diff --git a/extensions/emmet/tsconfig.json b/extensions/emmet/tsconfig.json index ad07467f4ea..994fa239537 100644 --- a/extensions/emmet/tsconfig.json +++ b/extensions/emmet/tsconfig.json @@ -9,7 +9,6 @@ ], "include": [ "src/**/*", - "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts", + "../../src/vscode-dts/vscode.d.ts" ] } From 548da541d84633b5c1de85d0ab7f3ea83054cb43 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 19 Apr 2022 19:08:50 -0700 Subject: [PATCH 157/245] fix #146406 --- package.json | 6 +++--- remote/package.json | 6 +++--- remote/web/package.json | 4 ++-- remote/web/yarn.lock | 16 ++++++++-------- remote/yarn.lock | 24 ++++++++++++------------ yarn.lock | 24 ++++++++++++------------ 6 files changed, 40 insertions(+), 40 deletions(-) diff --git a/package.json b/package.json index 550e28ce0b7..cf2af1650a3 100644 --- a/package.json +++ b/package.json @@ -84,12 +84,12 @@ "vscode-proxy-agent": "^0.12.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.23", + "xterm": "4.19.0-beta.24", "xterm-addon-search": "0.9.0-beta.21", "xterm-addon-serialize": "0.7.0-beta.12", "xterm-addon-unicode11": "0.4.0-beta.3", - "xterm-addon-webgl": "0.12.0-beta.27", - "xterm-headless": "4.19.0-beta.22", + "xterm-addon-webgl": "0.12.0-beta.28", + "xterm-headless": "4.19.0-beta.24", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/package.json b/remote/package.json index 548e42fe932..0424596dfd3 100644 --- a/remote/package.json +++ b/remote/package.json @@ -24,12 +24,12 @@ "vscode-proxy-agent": "^0.12.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.23", + "xterm": "4.19.0-beta.24", "xterm-addon-search": "0.9.0-beta.21", "xterm-addon-serialize": "0.7.0-beta.12", "xterm-addon-unicode11": "0.4.0-beta.3", - "xterm-addon-webgl": "0.12.0-beta.27", - "xterm-headless": "4.19.0-beta.22", + "xterm-addon-webgl": "0.12.0-beta.28", + "xterm-headless": "4.19.0-beta.24", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/web/package.json b/remote/web/package.json index 5c1648f1633..b2543988547 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -10,9 +10,9 @@ "tas-client-umd": "0.1.4", "vscode-oniguruma": "1.6.1", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.23", + "xterm": "4.19.0-beta.24", "xterm-addon-search": "0.9.0-beta.21", "xterm-addon-unicode11": "0.4.0-beta.3", - "xterm-addon-webgl": "0.12.0-beta.27" + "xterm-addon-webgl": "0.12.0-beta.28" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 05f9a318136..2094e08a208 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -123,12 +123,12 @@ 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.27: - version "0.12.0-beta.27" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.27.tgz#afc5bc01d1ef3af9005fb9f6325a4db9c92aa8d9" - integrity sha512-P948trotU8FMHtaA7C2x97VpLq6QLSjO53kWNvONS0/XwEKQBIYCI7Jfri2wcLgfQg6Cn4OQGLoj2YBK3MMyww== +xterm-addon-webgl@0.12.0-beta.28: + version "0.12.0-beta.28" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.28.tgz#252f88fd15816c23789659a0e2545682cf1eec9c" + integrity sha512-xpqZRYlyv+aNdDl46W2Hi2fxakNvdJDmWhOwGHPjOmex+kOYdBvVn4rRZmJ7xrKFuQVOfzb3SQCmpZ/njCpBJA== -xterm@4.19.0-beta.23: - version "4.19.0-beta.23" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.23.tgz#d5a154530c0f7ea05998efad5c9937f4a111ff57" - integrity sha512-WA0thNR2r1OtEQ0qCwxcfL8PZ6EdWWXHbgIp/3PMhWoxVKdBmg5nntJGnmFB5ZVnp5OlQbAymcgFlG8KnNzwag== +xterm@4.19.0-beta.24: + version "4.19.0-beta.24" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.24.tgz#12b62877d4cf133f4626db3a15f6ed267b3bbcb5" + integrity sha512-GydvC2ExdsfjkiXhUVuZtDhc89IZfyMLFgw/nwWHrvkyq4iCVsHbMloEObV3lZmv+0oRpcGtTiorfzdWGiHG0A== diff --git a/remote/yarn.lock b/remote/yarn.lock index af39af365da..3bd6b395860 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -929,20 +929,20 @@ 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.27: - version "0.12.0-beta.27" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.27.tgz#afc5bc01d1ef3af9005fb9f6325a4db9c92aa8d9" - integrity sha512-P948trotU8FMHtaA7C2x97VpLq6QLSjO53kWNvONS0/XwEKQBIYCI7Jfri2wcLgfQg6Cn4OQGLoj2YBK3MMyww== +xterm-addon-webgl@0.12.0-beta.28: + version "0.12.0-beta.28" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.28.tgz#252f88fd15816c23789659a0e2545682cf1eec9c" + integrity sha512-xpqZRYlyv+aNdDl46W2Hi2fxakNvdJDmWhOwGHPjOmex+kOYdBvVn4rRZmJ7xrKFuQVOfzb3SQCmpZ/njCpBJA== -xterm-headless@4.19.0-beta.22: - version "4.19.0-beta.22" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.22.tgz#23a3ad3f2dcacb43f595dc913afc116a3a60bd45" - integrity sha512-ICueI3qusYo5ozQ9irM1A3vHhWq1VhHvyYK4Qo08m9uVTF3tUU6whklgmXEMSFvZhlSpS4OTW3l4gIa5V4db/Q== +xterm-headless@4.19.0-beta.24: + version "4.19.0-beta.24" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.24.tgz#17dab68ae1b2600b0926d4c47032599f3b2f24cc" + integrity sha512-Coq8gQ9f5/kNr1yhpXTZqatTOHOYDF4XVp93SM12vRROH64bM0rUMec6ukuTAd8DD97UOWYUgR+n7+xD0iTTSg== -xterm@4.19.0-beta.23: - version "4.19.0-beta.23" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.23.tgz#d5a154530c0f7ea05998efad5c9937f4a111ff57" - integrity sha512-WA0thNR2r1OtEQ0qCwxcfL8PZ6EdWWXHbgIp/3PMhWoxVKdBmg5nntJGnmFB5ZVnp5OlQbAymcgFlG8KnNzwag== +xterm@4.19.0-beta.24: + version "4.19.0-beta.24" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.24.tgz#12b62877d4cf133f4626db3a15f6ed267b3bbcb5" + integrity sha512-GydvC2ExdsfjkiXhUVuZtDhc89IZfyMLFgw/nwWHrvkyq4iCVsHbMloEObV3lZmv+0oRpcGtTiorfzdWGiHG0A== yallist@^4.0.0: version "4.0.0" diff --git a/yarn.lock b/yarn.lock index 375d6d3be40..b298ec5a768 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12219,20 +12219,20 @@ 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.27: - version "0.12.0-beta.27" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.27.tgz#afc5bc01d1ef3af9005fb9f6325a4db9c92aa8d9" - integrity sha512-P948trotU8FMHtaA7C2x97VpLq6QLSjO53kWNvONS0/XwEKQBIYCI7Jfri2wcLgfQg6Cn4OQGLoj2YBK3MMyww== +xterm-addon-webgl@0.12.0-beta.28: + version "0.12.0-beta.28" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.28.tgz#252f88fd15816c23789659a0e2545682cf1eec9c" + integrity sha512-xpqZRYlyv+aNdDl46W2Hi2fxakNvdJDmWhOwGHPjOmex+kOYdBvVn4rRZmJ7xrKFuQVOfzb3SQCmpZ/njCpBJA== -xterm-headless@4.19.0-beta.22: - version "4.19.0-beta.22" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.22.tgz#23a3ad3f2dcacb43f595dc913afc116a3a60bd45" - integrity sha512-ICueI3qusYo5ozQ9irM1A3vHhWq1VhHvyYK4Qo08m9uVTF3tUU6whklgmXEMSFvZhlSpS4OTW3l4gIa5V4db/Q== +xterm-headless@4.19.0-beta.24: + version "4.19.0-beta.24" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.24.tgz#17dab68ae1b2600b0926d4c47032599f3b2f24cc" + integrity sha512-Coq8gQ9f5/kNr1yhpXTZqatTOHOYDF4XVp93SM12vRROH64bM0rUMec6ukuTAd8DD97UOWYUgR+n7+xD0iTTSg== -xterm@4.19.0-beta.23: - version "4.19.0-beta.23" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.23.tgz#d5a154530c0f7ea05998efad5c9937f4a111ff57" - integrity sha512-WA0thNR2r1OtEQ0qCwxcfL8PZ6EdWWXHbgIp/3PMhWoxVKdBmg5nntJGnmFB5ZVnp5OlQbAymcgFlG8KnNzwag== +xterm@4.19.0-beta.24: + version "4.19.0-beta.24" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.24.tgz#12b62877d4cf133f4626db3a15f6ed267b3bbcb5" + integrity sha512-GydvC2ExdsfjkiXhUVuZtDhc89IZfyMLFgw/nwWHrvkyq4iCVsHbMloEObV3lZmv+0oRpcGtTiorfzdWGiHG0A== y18n@^3.2.1: version "3.2.2" From 973ebbcdd819de48edf20595f099b4f4998d11a9 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 19 Apr 2022 19:07:41 -0700 Subject: [PATCH 158/245] =?UTF-8?q?Better=20spelling=20=20=F0=9F=AA=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/vscode-dts/vscode.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index 61f5c32961e..9d057568ff9 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -9420,7 +9420,7 @@ declare module 'vscode' { * If language id is not provided, then **Log** is used as default language id. * * You can access the visible or active output channel as a {@link TextDocument text document} from {@link window.visibleTextEditors visible editors} or {@link window.activeTextEditor active editor} - * and use the langrage id to contribute language features like syntax coloring, code lens etc., + * and use the language id to contribute language features like syntax coloring, code lens etc., * * @param name Human-readable string which will be used to represent the channel in the UI. * @param languageId The identifier of the language associated with the channel. From d851ea5d49b0ec89b98765d9e0084c25f2ff5031 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 19 Apr 2022 19:31:58 -0700 Subject: [PATCH 159/245] Add 'go to source definition' command Fixes #147532 Requires TS 4.7+ --- .../typescript-language-features/package.json | 32 +++++ .../package.nls.json | 1 + .../src/languageFeatures/sourceDefinition.ts | 122 ++++++++++++++++++ .../src/languageProvider.ts | 1 + .../src/utils/api.ts | 1 + 5 files changed, 157 insertions(+) create mode 100644 extensions/typescript-language-features/src/languageFeatures/sourceDefinition.ts diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index db88efe88ca..932626b6a12 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -65,6 +65,7 @@ "onCommand:_typescript.configurePlugin", "onCommand:_typescript.learnMoreAboutRefactorings", "onCommand:typescript.fileReferences", + "onCommand:typescript.goToSourceDefinition", "onTaskType:typescript", "onLanguage:jsonc" ], @@ -1229,6 +1230,11 @@ "command": "typescript.findAllFileReferences", "title": "%typescript.findAllFileReferences%", "category": "TypeScript" + }, + { + "command": "typescript.goToSourceDefinition", + "title": "%typescript.goToSourceDefinition%", + "category": "TypeScript" } ], "menus": { @@ -1280,6 +1286,32 @@ { "command": "typescript.findAllFileReferences", "when": "tsSupportsFileReferences && typescript.isManagedFile" + }, + { + "command": "typescript.goToSourceDefinition", + "when": "tsSupportsSourceDefinition && typescript.isManagedFile" + } + ], + "editor/context": [ + { + "command": "typescript.goToSourceDefinition", + "when": "tsSupportsSourceDefinition && resourceLangId == typescript", + "group": "navigation@9" + }, + { + "command": "typescript.goToSourceDefinition", + "when": "tsSupportsSourceDefinition && resourceLangId == typescriptreact", + "group": "navigation@9" + }, + { + "command": "typescript.goToSourceDefinition", + "when": "tsSupportsSourceDefinition && resourceLangId == javascript", + "group": "navigation@9" + }, + { + "command": "typescript.goToSourceDefinition", + "when": "tsSupportsSourceDefinition && resourceLangId == javascriptreact", + "group": "navigation@9" } ], "explorer/context": [ diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index 4a7322a8432..5a91821e729 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -190,6 +190,7 @@ "codeActions.refactor.rewrite.property.generateAccessors.description": "Generate 'get' and 'set' accessors", "codeActions.source.organizeImports.title": "Organize imports", "typescript.findAllFileReferences": "Find File References", + "typescript.goToSourceDefinition": "Go to Source Definition", "configuration.suggest.classMemberSnippets.enabled": "Enable/disable snippet completions for class members. Requires using TypeScript 4.5+ in the workspace", "configuration.suggest.objectLiteralMethodSnippets.enabled": "Enable/disable snippet completions for methods in object literals. Requires using TypeScript 4.7+ in the workspace" } diff --git a/extensions/typescript-language-features/src/languageFeatures/sourceDefinition.ts b/extensions/typescript-language-features/src/languageFeatures/sourceDefinition.ts new file mode 100644 index 00000000000..8107d92b109 --- /dev/null +++ b/extensions/typescript-language-features/src/languageFeatures/sourceDefinition.ts @@ -0,0 +1,122 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as nls from 'vscode-nls'; +import { Command, CommandManager } from '../commands/commandManager'; +import * as Proto from '../protocol'; +import { ExecConfig, ITypeScriptServiceClient, ServerResponse } 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 { + + public static readonly context = 'tsSupportsSourceDefinition'; + public static readonly minVersion = API.v470; + + public readonly id = 'typescript.goToSourceDefinition'; + + public constructor( + private readonly client: ITypeScriptServiceClient + ) { } + + public async execute() { + if (this.client.apiVersion.lt(SourceDefinitionCommand.minVersion)) { + vscode.window.showErrorMessage(localize('error.unsupportedVersion', "Go to Source Definition failed. Requires TypeScript 4.2+.")); + return; + } + + const activeEditor = vscode.window.activeTextEditor; + if (!activeEditor) { + vscode.window.showErrorMessage(localize('error.noResource', "Go to Source Definition failed. No resource provided.")); + return; + } + + const resource = activeEditor.document.uri; + const document = await vscode.workspace.openTextDocument(resource); + if (!isSupportedLanguageMode(document)) { + vscode.window.showErrorMessage(localize('error.unsupportedLanguage', "Go to Source Definition failed. Unsupported file type.")); + return; + } + + const openedFiledPath = this.client.toOpenedFilePath(document); + if (!openedFiledPath) { + vscode.window.showErrorMessage(localize('error.unknownFile', "Go to Source Definition failed. Unknown file type.")); + return; + } + + await vscode.window.withProgress({ + location: vscode.ProgressLocation.Window, + title: localize('progress.title', "Finding source definitions") + }, async (_progress, token) => { + + const position = activeEditor.selection.anchor; + const args = typeConverters.Position.toFileLocationRequestArgs(openedFiledPath, position); + const response = await (this.client as ExperimentalProto.IExtendedTypeScriptServiceClient).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)); + + if (locations.length) { + if (locations.length === 1) { + vscode.commands.executeCommand('vscode.open', locations[0].uri.with({ + fragment: `L${locations[0].range.start.line + 1},${locations[0].range.start.character + 1}` + })); + } else { + vscode.commands.executeCommand('editor.action.showReferences', resource, position, locations); + } + return; + } + } + + vscode.window.showErrorMessage(localize('error.noReferences', "No source definitions found.")); + }); + } +} + + +export function register( + client: ITypeScriptServiceClient, + commandManager: CommandManager +) { + function updateContext() { + vscode.commands.executeCommand('setContext', SourceDefinitionCommand.context, client.apiVersion.gte(SourceDefinitionCommand.minVersion)); + } + updateContext(); + + commandManager.register(new SourceDefinitionCommand(client)); + return client.onTsServerStarted(() => updateContext()); +} diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index 4a3669e142d..fa2ca6563b4 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -82,6 +82,7 @@ export default class LanguageProvider extends Disposable { import('./languageFeatures/semanticTokens').then(provider => this._register(provider.register(selector, this.client))), import('./languageFeatures/signatureHelp').then(provider => this._register(provider.register(selector, this.client))), import('./languageFeatures/smartSelect').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/sourceDefinition').then(provider => this._register(provider.register(this.client, this.commandManager))), import('./languageFeatures/tagClosing').then(provider => this._register(provider.register(selector, this.description, this.client))), import('./languageFeatures/typeDefinitions').then(provider => this._register(provider.register(selector, this.client))), import('./languageFeatures/inlayHints').then(provider => this._register(provider.register(selector, this.description, this.client, this.fileConfigurationManager))), diff --git a/extensions/typescript-language-features/src/utils/api.ts b/extensions/typescript-language-features/src/utils/api.ts index e4c6133cc15..e9e85091fa4 100644 --- a/extensions/typescript-language-features/src/utils/api.ts +++ b/extensions/typescript-language-features/src/utils/api.ts @@ -39,6 +39,7 @@ export default class API { public static readonly v430 = API.fromSimpleString('4.3.0'); public static readonly v440 = API.fromSimpleString('4.4.0'); public static readonly v460 = API.fromSimpleString('4.6.0'); + public static readonly v470 = API.fromSimpleString('4.7.0'); public static fromVersionString(versionString: string): API { let version = semver.valid(versionString); From d472403e4294fd9aebd4d92074a03aad0fb83d9d Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 19 Apr 2022 19:43:01 -0700 Subject: [PATCH 160/245] update min contrast terminal setting description to include excluded powerline chars --- .../workbench/contrib/terminal/common/terminalConfiguration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index 4020b3e6fa0..3ddae516bb0 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -186,7 +186,7 @@ const terminalConfiguration: IConfigurationNode = { default: DEFAULT_LINE_HEIGHT }, [TerminalSettingId.MinimumContrastRatio]: { - markdownDescription: localize('terminal.integrated.minimumContrastRatio', "When set the foreground color of each cell will change to try meet the contrast ratio specified. Example values:\n\n- 1: Do nothing and use the standard theme colors.\n- 4.5: [WCAG AA compliance (minimum)](https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html) (default).\n- 7: [WCAG AAA compliance (enhanced)](https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast7.html).\n- 21: White on black or black on white."), + markdownDescription: localize('terminal.integrated.minimumContrastRatio', "When set, the foreground color of each cell will change to try meet the contrast ratio specified. Note that this will not apply to `powerline` characters per #146406. Example values:\n\n- 1: Do nothing and use the standard theme colors.\n- 4.5: [WCAG AA compliance (minimum)](https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-contrast.html) (default).\n- 7: [WCAG AAA compliance (enhanced)](https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast7.html).\n- 21: White on black or black on white."), type: 'number', default: 4.5 }, From 89d10043719b48a6e6e5978fe61e93fd2c5a5d82 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 19 Apr 2022 20:07:27 -0700 Subject: [PATCH 161/245] Fix #145265 --- src/vs/workbench/contrib/debug/node/terminals.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/node/terminals.ts b/src/vs/workbench/contrib/debug/node/terminals.ts index 5f89e708bc7..b6c3ced13c5 100644 --- a/src/vs/workbench/contrib/debug/node/terminals.ts +++ b/src/vs/workbench/contrib/debug/node/terminals.ts @@ -121,7 +121,8 @@ export function prepareCommand(shell: string, args: string[], cwd?: string, env? quote = (s: string) => { s = s.replace(/\"/g, '""'); - return (' "> s.includes(char)) || s.length === 0) ? `"${s}"` : s; + s = s.replace(/([> s.includes(char)) || s.length === 0) ? `"${s}"` : s; }; if (cwd) { From 5931aa8ed61872919d3e3bba33a60d316687ebe1 Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 20 Apr 2022 09:47:06 +0200 Subject: [PATCH 162/245] fix two issues with notebookType and document selectors, (1) make sure that `notebookType: *` matches only when a notebook is around, (2) include `notebookType`-property when converting from extHost type to renderer type, fyi @dbaeumer --- src/vs/editor/common/languageSelector.ts | 2 +- .../common/modes/languageSelector.test.ts | 19 +++++++++++++++++++ .../api/common/extHostTypeConverters.ts | 3 ++- .../test/browser/extHostTypeConverter.test.ts | 14 +++++++++++++- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/common/languageSelector.ts b/src/vs/editor/common/languageSelector.ts index 2ef0be7ee10..64a8440059f 100644 --- a/src/vs/editor/common/languageSelector.ts +++ b/src/vs/editor/common/languageSelector.ts @@ -87,7 +87,7 @@ export function score(selector: LanguageSelector | undefined, candidateUri: URI, if (notebookType) { if (notebookType === candidateNotebookType) { ret = 10; - } else if (notebookType === '*') { + } else if (notebookType === '*' && candidateNotebookType !== undefined) { ret = Math.max(ret, 5); } else { return 0; diff --git a/src/vs/editor/test/common/modes/languageSelector.test.ts b/src/vs/editor/test/common/modes/languageSelector.test.ts index e2d59071382..53cba5e386e 100644 --- a/src/vs/editor/test/common/modes/languageSelector.test.ts +++ b/src/vs/editor/test/common/modes/languageSelector.test.ts @@ -131,4 +131,23 @@ suite('LanguageSelector', function () { let value = score(selector, URI.file('/home/user/Desktop/test.json'), 'json', true, undefined); assert.strictEqual(value, 10); }); + + test('NotebookType without notebook', function () { + let obj = { + uri: URI.parse('file:///my/file.bat'), + langId: 'bat', + }; + + let value = score({ + language: 'bat', + notebookType: 'xxx' + }, obj.uri, obj.langId, true, undefined); + assert.strictEqual(value, 0); + + value = score({ + language: 'bat', + notebookType: '*' + }, obj.uri, obj.langId, true, undefined); + assert.strictEqual(value, 0); + }); }); diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index d39678dd978..979eebec86c 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1491,7 +1491,8 @@ export namespace LanguageSelector { language: filter.language, scheme: filter.scheme, pattern: GlobPattern.from(filter.pattern), - exclusive: filter.exclusive + exclusive: filter.exclusive, + notebookType: filter.notebookType }; } } diff --git a/src/vs/workbench/api/test/browser/extHostTypeConverter.test.ts b/src/vs/workbench/api/test/browser/extHostTypeConverter.test.ts index 20ef8c11b6c..fb2b02bc890 100644 --- a/src/vs/workbench/api/test/browser/extHostTypeConverter.test.ts +++ b/src/vs/workbench/api/test/browser/extHostTypeConverter.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; -import { MarkdownString, NotebookCellOutputItem, NotebookData } from 'vs/workbench/api/common/extHostTypeConverters'; +import { MarkdownString, NotebookCellOutputItem, NotebookData, LanguageSelector } from 'vs/workbench/api/common/extHostTypeConverters'; import { isEmptyObject } from 'vs/base/common/types'; import { forEach } from 'vs/base/common/collections'; import { LogLevel as _MainLogLevel } from 'vs/platform/log/common/log'; @@ -111,4 +111,16 @@ suite('ExtHostTypeConverter', function () { assert.strictEqual(item2.mime, item.mime); assert.deepStrictEqual(Array.from(item2.data), Array.from(item.data)); }); + + test('LanguageSelector', function () { + const out = LanguageSelector.from({ language: 'bat', notebookType: 'xxx' }); + assert.ok(typeof out === 'object'); + assert.deepStrictEqual(out, { + language: 'bat', + notebookType: 'xxx', + scheme: undefined, + pattern: undefined, + exclusive: undefined, + }); + }); }); From 931f4df9dca8f4e82d13f6aeb0506723427271e0 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Apr 2022 11:27:40 +0200 Subject: [PATCH 163/245] fixes #147757 --- src/vs/base/common/product.ts | 1 + src/vs/code/electron-main/app.ts | 2 +- .../electron-main/abstractUpdateService.ts | 12 +++++--- .../electron-main/updateService.darwin.ts | 4 +-- .../electron-main/updateService.win32.ts | 28 +++++++++++++++++-- 5 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/vs/base/common/product.ts b/src/vs/base/common/product.ts index fd05d5a160c..c66cf41aa6c 100644 --- a/src/vs/base/common/product.ts +++ b/src/vs/base/common/product.ts @@ -41,6 +41,7 @@ export interface IProductConfiguration { readonly win32AppUserModelId?: string; readonly win32MutexName?: string; + readonly win32RegValueName?: string; readonly applicationName: string; readonly embedderIdentifier?: string; diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index eec246062ff..19fa65f56dc 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -1141,7 +1141,7 @@ export class CodeApplication extends Disposable { // Initialize update service const updateService = accessor.get(IUpdateService); if (updateService instanceof Win32UpdateService || updateService instanceof LinuxUpdateService || updateService instanceof DarwinUpdateService) { - updateService.initialize(); + await updateService.initialize(); } // Start to fetch shell environment (if needed) after window has opened diff --git a/src/vs/platform/update/electron-main/abstractUpdateService.ts b/src/vs/platform/update/electron-main/abstractUpdateService.ts index 5adf20c74e4..86737bd1657 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts @@ -57,7 +57,7 @@ export abstract class AbstractUpdateService implements IUpdateService { * optimization, to avoid using extra CPU cycles before first window open. * https://github.com/microsoft/vscode/issues/89784 */ - initialize(): void { + async initialize(): Promise { if (!this.environmentMainService.isBuilt) { return; // updates are never enabled when running out of sources } @@ -72,7 +72,7 @@ export abstract class AbstractUpdateService implements IUpdateService { return; } - const updateMode = this.getUpdateMode(); + const updateMode = await this.getUpdateMode(); const quality = this.getProductQuality(updateMode); if (!quality) { @@ -104,7 +104,7 @@ export abstract class AbstractUpdateService implements IUpdateService { } } - private getUpdateMode(): 'none' | 'manual' | 'start' | 'default' { + protected async getUpdateMode(): Promise<'none' | 'manual' | 'start' | 'default'> { return getMigratedSettingValue<'none' | 'manual' | 'start' | 'default'>(this.configurationService, 'update.mode', 'update.channel'); } @@ -184,7 +184,11 @@ export abstract class AbstractUpdateService implements IUpdateService { async isLatestVersion(): Promise { if (!this.url) { return undefined; - } else if (this.getUpdateMode() === 'none') { + } + + const mode = await this.getUpdateMode(); + + if (mode === 'none') { return false; } diff --git a/src/vs/platform/update/electron-main/updateService.darwin.ts b/src/vs/platform/update/electron-main/updateService.darwin.ts index 0c008ea8504..688ea356ef1 100644 --- a/src/vs/platform/update/electron-main/updateService.darwin.ts +++ b/src/vs/platform/update/electron-main/updateService.darwin.ts @@ -38,8 +38,8 @@ export class DarwinUpdateService extends AbstractUpdateService { super(lifecycleMainService, configurationService, environmentMainService, requestService, logService, productService); } - override initialize(): void { - super.initialize(); + override async initialize(): Promise { + await super.initialize(); this.onRawError(this.onError, this, this.disposables); this.onRawUpdateAvailable(this.onUpdateAvailable, this, this.disposables); this.onRawUpdateDownloaded(this.onUpdateDownloaded, this, this.disposables); diff --git a/src/vs/platform/update/electron-main/updateService.win32.ts b/src/vs/platform/update/electron-main/updateService.win32.ts index 781e766b7f7..46d52295eeb 100644 --- a/src/vs/platform/update/electron-main/updateService.win32.ts +++ b/src/vs/platform/update/electron-main/updateService.win32.ts @@ -47,6 +47,14 @@ function getUpdateType(): UpdateType { return _updateType; } +function validateUpdateModeValue(value: string | undefined): 'none' | 'manual' | 'start' | 'default' | undefined { + if (value === 'none' || value === 'manual' || value === 'start' || value === 'default') { + return value; + } else { + return undefined; + } +} + export class Win32UpdateService extends AbstractUpdateService { private availableUpdate: IAvailableUpdate | undefined; @@ -71,8 +79,24 @@ export class Win32UpdateService extends AbstractUpdateService { super(lifecycleMainService, configurationService, environmentMainService, requestService, logService, productService); } - override initialize(): void { - super.initialize(); + protected override async getUpdateMode(): Promise<'none' | 'manual' | 'start' | 'default'> { + if (this.productService.win32RegValueName) { + const policyKey = `Software\\Policies\\Microsoft\\${this.productService.win32RegValueName}`; + const [hklm, hkcu] = await Promise.all([ + this.nativeHostMainService.windowsGetStringRegKey(undefined, 'HKEY_LOCAL_MACHINE', policyKey, 'UpdateMode').then(validateUpdateModeValue), + this.nativeHostMainService.windowsGetStringRegKey(undefined, 'HKEY_CURRENT_USER', policyKey, 'UpdateMode').then(validateUpdateModeValue) + ]); + + if (hklm) { + this.logService.trace(`update#getUpdateMode: 'UpdateMode' policy defined in 'HKLM\\${policyKey}':`, hklm); + return hklm; + } else if (hkcu) { + this.logService.trace(`update#getUpdateMode: 'UpdateMode' policy defined in 'HKCU\\${policyKey}':`, hkcu); + return hkcu; + } + } + + return await super.getUpdateMode(); } protected buildUpdateFeedUrl(quality: string): string | undefined { From 8783c56eb759610699231c0dffd97b5e168bf9cc Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 20 Apr 2022 11:30:31 +0200 Subject: [PATCH 164/245] fix https://github.com/microsoft/vscode/issues/147711 --- .../src/singlefolder-tests/notebook.test.ts | 5 ++++- src/vs/workbench/api/browser/mainThreadNotebook.ts | 6 +++--- src/vs/workbench/api/common/extHostNotebook.ts | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) 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 c120526ae14..462e3113698 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts @@ -1001,7 +1001,9 @@ suite('Notebook & LiveShare', function () { suiteDisposables.push(vscode.workspace.registerNotebookSerializer(notebookType, new class implements vscode.NotebookSerializer { deserializeNotebook(content: Uint8Array, _token: vscode.CancellationToken): vscode.NotebookData | Thenable { const value = new TextDecoder().decode(content); - return new vscode.NotebookData([new vscode.NotebookCellData(vscode.NotebookCellKind.Code, value, 'fooLang')]); + const cell1 = new vscode.NotebookCellData(vscode.NotebookCellKind.Code, value, 'fooLang'); + cell1.outputs = [new vscode.NotebookCellOutput([vscode.NotebookCellOutputItem.stderr(value)])]; + return new vscode.NotebookData([cell1]); } serializeNotebook(data: vscode.NotebookData, _token: vscode.CancellationToken): Uint8Array | Thenable { return new TextEncoder().encode(data.cells[0].value); @@ -1030,6 +1032,7 @@ suite('Notebook & LiveShare', function () { assert.ok(data instanceof vscode.NotebookData); assert.strictEqual(data.cells.length, 1); assert.strictEqual(data.cells[0].value, value); + assert.strictEqual(new TextDecoder().decode(data.cells[0].outputs![0].items[0].data), value); }); test('command: vscode.executeNotebookToData', async function () { diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index b22dce389de..6b522da7c97 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -200,14 +200,14 @@ CommandsRegistry.registerCommand('_executeDataToNotebook', async (accessor, ...a } const dto = await info.serializer.dataToNotebook(bytes); - return NotebookDto.toNotebookDataDto(dto); + return new SerializableObjectWithBuffers(NotebookDto.toNotebookDataDto(dto)); }); CommandsRegistry.registerCommand('_executeNotebookToData', async (accessor, ...args) => { const [notebookType, dto] = args; assertType(typeof notebookType === 'string', 'string'); - assertType(typeof dto === 'object', 'NotebookDataDto'); + assertType(typeof dto === 'object'); const notebookService = accessor.get(INotebookService); const info = await notebookService.withNotebookDataProvider(notebookType); @@ -215,7 +215,7 @@ CommandsRegistry.registerCommand('_executeNotebookToData', async (accessor, ...a return; } - const data = NotebookDto.fromNotebookDataDto(dto); + const data = NotebookDto.fromNotebookDataDto(dto.value); const bytes = await info.serializer.notebookToData(data); return bytes; }); diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 6d68184d52c..7c9cc4c0eb0 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -523,12 +523,12 @@ export class ExtHostNotebookController implements ExtHostNotebookShape { const commandDataToNotebook = new ApiCommand( 'vscode.executeDataToNotebook', '_executeDataToNotebook', 'Invoke notebook serializer', [notebookTypeArg, new ApiCommandArgument('data', 'Bytes to convert to data', v => v instanceof Uint8Array, v => VSBuffer.wrap(v))], - new ApiCommandResult('Notebook Data', dto => typeConverters.NotebookData.to(dto)) + new ApiCommandResult, vscode.NotebookData>('Notebook Data', data => typeConverters.NotebookData.to(data.value)) ); const commandNotebookToData = new ApiCommand( 'vscode.executeNotebookToData', '_executeNotebookToData', 'Invoke notebook serializer', - [notebookTypeArg, new ApiCommandArgument('NotebookData', 'Notebook data to convert to bytes', v => true, v => typeConverters.NotebookData.from(v))], + [notebookTypeArg, new ApiCommandArgument>('NotebookData', 'Notebook data to convert to bytes', v => true, v => new SerializableObjectWithBuffers(typeConverters.NotebookData.from(v)))], new ApiCommandResult('Bytes', dto => dto.buffer) ); From 701ca68369370db76fb0204cb0be1f02f3290fa1 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Apr 2022 11:31:58 +0200 Subject: [PATCH 165/245] use info instead of trace --- src/vs/platform/update/electron-main/updateService.win32.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/update/electron-main/updateService.win32.ts b/src/vs/platform/update/electron-main/updateService.win32.ts index 46d52295eeb..e3661ce4bbc 100644 --- a/src/vs/platform/update/electron-main/updateService.win32.ts +++ b/src/vs/platform/update/electron-main/updateService.win32.ts @@ -88,10 +88,10 @@ export class Win32UpdateService extends AbstractUpdateService { ]); if (hklm) { - this.logService.trace(`update#getUpdateMode: 'UpdateMode' policy defined in 'HKLM\\${policyKey}':`, hklm); + this.logService.info(`update#getUpdateMode: 'UpdateMode' policy defined in 'HKLM\\${policyKey}':`, hklm); return hklm; } else if (hkcu) { - this.logService.trace(`update#getUpdateMode: 'UpdateMode' policy defined in 'HKCU\\${policyKey}':`, hkcu); + this.logService.info(`update#getUpdateMode: 'UpdateMode' policy defined in 'HKCU\\${policyKey}':`, hkcu); return hkcu; } } From a9c39c582c0dda500914a21a06b1286174b69511 Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 20 Apr 2022 12:50:54 +0200 Subject: [PATCH 166/245] rename type, add jsdoc https://github.com/microsoft/vscode/issues/144662 --- .../workbench/api/common/extHostNotebookDocument.ts | 4 ++-- .../vscode.proposed.notebookDocumentEvents.d.ts | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/api/common/extHostNotebookDocument.ts b/src/vs/workbench/api/common/extHostNotebookDocument.ts index e8cc8e027ff..b77133347e3 100644 --- a/src/vs/workbench/api/common/extHostNotebookDocument.ts +++ b/src/vs/workbench/api/common/extHostNotebookDocument.ts @@ -218,11 +218,11 @@ export class ExtHostNotebookDocument { const result = { notebook: this.apiNotebook, metadata: newMetadata, - cellChanges: [], + cellChanges: [], contentChanges: [], }; - type RelaxedCellChange = Partial & { cell: vscode.NotebookCell }; + type RelaxedCellChange = Partial & { cell: vscode.NotebookCell }; const relaxedCellChanges: RelaxedCellChange[] = []; // -- apply change and populate content changes diff --git a/src/vscode-dts/vscode.proposed.notebookDocumentEvents.d.ts b/src/vscode-dts/vscode.proposed.notebookDocumentEvents.d.ts index f02680f147d..86b755852eb 100644 --- a/src/vscode-dts/vscode.proposed.notebookDocumentEvents.d.ts +++ b/src/vscode-dts/vscode.proposed.notebookDocumentEvents.d.ts @@ -12,7 +12,7 @@ declare module 'vscode' { * * @see {@link NotebookDocumentChangeEvent} */ - export interface NotebookDocumentContentCellChange { + export interface NotebookDocumentCellChange { /** * The affected notebook. @@ -44,7 +44,7 @@ declare module 'vscode' { } /** - * Describes a structural change to a notebook document. + * Describes a structural change to a notebook document, e.g new added and removed cells. * * @see {@link NotebookDocumentChangeEvent} */ @@ -52,6 +52,9 @@ declare module 'vscode' { /** * The range at which cells have been either added or removed. + * + * Note that no cells have been {@link NotebookDocumentContentChange.removedCells removed} + * when this range is {@link NotebookRange.isEmpty empty}. */ readonly range: NotebookRange; @@ -87,9 +90,9 @@ declare module 'vscode' { readonly contentChanges: readonly NotebookDocumentContentChange[]; /** - * An array of {@link NotebookDocumentContentCellChange cell changes}. + * An array of {@link NotebookDocumentCellChange cell changes}. */ - readonly cellChanges: readonly NotebookDocumentContentCellChange[]; + readonly cellChanges: readonly NotebookDocumentCellChange[]; } export namespace workspace { From c7b2eeadb412fce7d61119cfe6e8c3761d5e95b1 Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 20 Apr 2022 12:51:45 +0200 Subject: [PATCH 167/245] fix typo, https://github.com/microsoft/vscode/issues/144662 --- src/vscode-dts/vscode.proposed.notebookDocumentEvents.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vscode-dts/vscode.proposed.notebookDocumentEvents.d.ts b/src/vscode-dts/vscode.proposed.notebookDocumentEvents.d.ts index 86b755852eb..1106e3f9786 100644 --- a/src/vscode-dts/vscode.proposed.notebookDocumentEvents.d.ts +++ b/src/vscode-dts/vscode.proposed.notebookDocumentEvents.d.ts @@ -44,7 +44,7 @@ declare module 'vscode' { } /** - * Describes a structural change to a notebook document, e.g new added and removed cells. + * Describes a structural change to a notebook document, e.g newly added and removed cells. * * @see {@link NotebookDocumentChangeEvent} */ From 36d7dd2a5767767a907d43e6cb833dfa25c02aee Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 20 Apr 2022 13:26:29 +0200 Subject: [PATCH 168/245] Inconsistent font size in a comment with `code` blocks (#146542) Fixes #146183 --- .../contrib/markdownRenderer/browser/markdownRenderer.ts | 5 +++++ .../contrib/comments/browser/commentThreadZoneWidget.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/markdownRenderer/browser/markdownRenderer.ts b/src/vs/editor/contrib/markdownRenderer/browser/markdownRenderer.ts index 2a323d76679..7220dbf1a4f 100644 --- a/src/vs/editor/contrib/markdownRenderer/browser/markdownRenderer.ts +++ b/src/vs/editor/contrib/markdownRenderer/browser/markdownRenderer.ts @@ -23,6 +23,7 @@ export interface IMarkdownRenderResult extends IDisposable { export interface IMarkdownRendererOptions { editor?: ICodeEditor; codeBlockFontFamily?: string; + codeBlockFontSize?: string; } /** @@ -93,6 +94,10 @@ export class MarkdownRenderer { element.style.fontFamily = this._options.codeBlockFontFamily; } + if (this._options.codeBlockFontSize !== undefined) { + element.style.fontSize = this._options.codeBlockFontSize; + } + return element; }, asyncRenderCallback: () => this._onDidRenderAsync.fire(), diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts index 0e41198ccb2..c6a047179b0 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts @@ -193,7 +193,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget this._scopedInstantiationService, this._commentThread as unknown as languages.CommentThread, this._pendingComment, - { editor: this.editor }, + { editor: this.editor, codeBlockFontSize: '' }, this._commentOptions, { actionRunner: () => { From 9530dca29df0e28cbd7030562f748fd167168d21 Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 20 Apr 2022 13:54:05 +0200 Subject: [PATCH 169/245] finalize `notebookDocumentEvents`-API, https://github.com/microsoft/vscode/issues/144662 --- extensions/ipynb/package.json | 3 +- extensions/ipynb/tsconfig.json | 3 +- extensions/vscode-api-tests/package.json | 1 - .../workbench/api/common/extHost.api.impl.ts | 2 - .../common/extensionsApiProposals.ts | 1 - src/vscode-dts/vscode.d.ts | 164 ++++++++++++++---- ...scode.proposed.notebookDocumentEvents.d.ts | 110 ------------ 7 files changed, 133 insertions(+), 151 deletions(-) delete mode 100644 src/vscode-dts/vscode.proposed.notebookDocumentEvents.d.ts diff --git a/extensions/ipynb/package.json b/extensions/ipynb/package.json index 414bedf2c52..c8feabc17af 100644 --- a/extensions/ipynb/package.json +++ b/extensions/ipynb/package.json @@ -10,8 +10,7 @@ }, "enabledApiProposals": [ "notebookEditor", - "notebookEditorEdit", - "notebookDocumentEvents" + "notebookEditorEdit" ], "activationEvents": [ "*" diff --git a/extensions/ipynb/tsconfig.json b/extensions/ipynb/tsconfig.json index b437665bd92..178e86493b4 100644 --- a/extensions/ipynb/tsconfig.json +++ b/extensions/ipynb/tsconfig.json @@ -10,7 +10,6 @@ "src/**/*", "../../src/vscode-dts/vscode.d.ts", "../../src/vscode-dts/vscode.proposed.notebookEditor.d.ts", - "../../src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts", - "../../src/vscode-dts/vscode.proposed.notebookDocumentEvents.d.ts", + "../../src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts" ] } diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 2a1bad68677..f1086bf1ba4 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -23,7 +23,6 @@ "notebookControllerKind", "notebookDebugOptions", "notebookDeprecated", - "notebookDocumentEvents", "notebookEditor", "notebookEditorDecorationType", "notebookEditorEdit", diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 6cb1dd4fdfb..1096349abd5 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -892,11 +892,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostNotebook.getNotebookDocument(uri).apiNotebook; }, onDidSaveNotebookDocument(listener, thisArg, disposables) { - checkProposedApiEnabled(extension, 'notebookDocumentEvents'); return extHostNotebookDocuments.onDidSaveNotebookDocument(listener, thisArg, disposables); }, onDidChangeNotebookDocument(listener, thisArg, disposables) { - checkProposedApiEnabled(extension, 'notebookDocumentEvents'); return extHostNotebookDocuments.onDidChangeNotebookDocument(listener, thisArg, disposables); }, get onDidOpenNotebookDocument(): Event { diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 04e760d97ce..074e8fbee5b 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -34,7 +34,6 @@ export const allApiProposals = Object.freeze({ notebookControllerKind: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookControllerKind.d.ts', notebookDebugOptions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookDebugOptions.d.ts', notebookDeprecated: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookDeprecated.d.ts', - notebookDocumentEvents: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookDocumentEvents.d.ts', notebookEditor: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookEditor.d.ts', notebookEditorDecorationType: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookEditorDecorationType.d.ts', notebookEditorEdit: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.notebookEditorEdit.d.ts', diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index 9d057568ff9..94f66962244 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -11721,6 +11721,16 @@ declare module 'vscode' { */ export function openNotebookDocument(notebookType: string, content?: NotebookData): Thenable; + /** + * An event that is emitted when a {@link NotebookDocument notebook} has changed. + */ + export const onDidChangeNotebookDocument: Event; + + /** + * An event that is emitted when a {@link NotebookDocument notebook} is saved. + */ + export const onDidSaveNotebookDocument: Event; + /** * Register a {@link NotebookSerializer notebook serializer}. * @@ -12440,6 +12450,39 @@ declare module 'vscode' { } + /** + * Represents a notebook editor that is attached to a {@link NotebookDocument notebook}. + * Additional properties of the NotebookEditor are available in the proposed + * API, which will be finalized later. + */ + export interface NotebookEditor { + + } + + /** + * Renderer messaging is used to communicate with a single renderer. It's returned from {@link notebooks.createRendererMessaging}. + */ + export interface NotebookRendererMessaging { + /** + * An event that fires when a message is received from a renderer. + */ + readonly onDidReceiveMessage: Event<{ + readonly editor: NotebookEditor; + readonly message: any; + }>; + + /** + * Send a message to one or all renderer. + * + * @param message Message to send + * @param editor Editor to target with the message. If not provided, the + * message is sent to all renderers. + * @returns a boolean indicating whether the message was successfully + * delivered to any renderer. + */ + postMessage(message: any, editor?: NotebookEditor): Thenable; + } + /** * A notebook cell kind. */ @@ -12503,39 +12546,6 @@ declare module 'vscode' { readonly executionSummary: NotebookCellExecutionSummary | undefined; } - /** - * Represents a notebook editor that is attached to a {@link NotebookDocument notebook}. - * Additional properties of the NotebookEditor are available in the proposed - * API, which will be finalized later. - */ - export interface NotebookEditor { - - } - - /** - * Renderer messaging is used to communicate with a single renderer. It's returned from {@link notebooks.createRendererMessaging}. - */ - export interface NotebookRendererMessaging { - /** - * An event that fires when a message is received from a renderer. - */ - readonly onDidReceiveMessage: Event<{ - readonly editor: NotebookEditor; - readonly message: any; - }>; - - /** - * Send a message to one or all renderer. - * - * @param message Message to send - * @param editor Editor to target with the message. If not provided, the - * message is sent to all renderers. - * @returns a boolean indicating whether the message was successfully - * delivered to any renderer. - */ - postMessage(message: any, editor?: NotebookEditor): Thenable; - } - /** * Represents a notebook which itself is a sequence of {@link NotebookCell code or markup cells}. Notebook documents are * created from {@link NotebookData notebook data}. @@ -12615,6 +12625,94 @@ declare module 'vscode' { save(): Thenable; } + /** + * Describes a change to a notebook cell. + * + * @see {@link NotebookDocumentChangeEvent} + */ + export interface NotebookDocumentCellChange { + + /** + * The affected notebook. + */ + readonly cell: NotebookCell; + + /** + * The document of the cell or `undefined` when it did not change. + * + * *Note* that you should use the {@link workspace.onDidChangeTextDocument onDidChangeTextDocument}-event + * for detailed change information, like what edits have been performed. + */ + readonly document: TextDocument | undefined; + + /** + * The new metadata of the cell or `undefined` when it did not change. + */ + readonly metadata: { [key: string]: any } | undefined; + + /** + * The new outputs of the cell or `undefined` when they did not change. + */ + readonly outputs: readonly NotebookCellOutput[] | undefined; + + /** + * The new execution summary of the cell or `undefined` when it did not change. + */ + readonly executionSummary: NotebookCellExecutionSummary | undefined; + } + + /** + * Describes a structural change to a notebook document, e.g newly added and removed cells. + * + * @see {@link NotebookDocumentChangeEvent} + */ + export interface NotebookDocumentContentChange { + + /** + * The range at which cells have been either added or removed. + * + * Note that no cells have been {@link NotebookDocumentContentChange.removedCells removed} + * when this range is {@link NotebookRange.isEmpty empty}. + */ + readonly range: NotebookRange; + + /** + * Cells that have been added to the document. + */ + readonly addedCells: readonly NotebookCell[]; + + /** + * Cells that have been removed from the document. + */ + readonly removedCells: readonly NotebookCell[]; + } + + /** + * An event describing a transactional {@link NotebookDocument notebook} change. + */ + export interface NotebookDocumentChangeEvent { + + /** + * The affected notebook. + */ + readonly notebook: NotebookDocument; + + /** + * The new metadata of the notebook or `undefined` when it did not change. + */ + readonly metadata: { [key: string]: any } | undefined; + + /** + * An array of content changes describing added or removed {@link NotebookCell cells}. + */ + readonly contentChanges: readonly NotebookDocumentContentChange[]; + + /** + * An array of {@link NotebookDocumentCellChange cell changes}. + */ + readonly cellChanges: readonly NotebookDocumentCellChange[]; + } + /** * The summary of a notebook cell execution. */ diff --git a/src/vscode-dts/vscode.proposed.notebookDocumentEvents.d.ts b/src/vscode-dts/vscode.proposed.notebookDocumentEvents.d.ts deleted file mode 100644 index 1106e3f9786..00000000000 --- a/src/vscode-dts/vscode.proposed.notebookDocumentEvents.d.ts +++ /dev/null @@ -1,110 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -// https://github.com/microsoft/vscode/issues/144662 - -declare module 'vscode' { - - /** - * Describes a change to a notebook cell. - * - * @see {@link NotebookDocumentChangeEvent} - */ - export interface NotebookDocumentCellChange { - - /** - * The affected notebook. - */ - readonly cell: NotebookCell; - - /** - * The document of the cell or `undefined` when it did not change. - * - * *Note* that you should use the {@link workspace.onDidChangeTextDocument onDidChangeTextDocument}-event - * for detailed change information, like what edits have been performed. - */ - readonly document: TextDocument | undefined; - - /** - * The new metadata of the cell or `undefined` when it did not change. - */ - readonly metadata: { [key: string]: any } | undefined; - - /** - * The new outputs of the cell or `undefined` when they did not change. - */ - readonly outputs: readonly NotebookCellOutput[] | undefined; - - /** - * The new execution summary of the cell or `undefined` when it did not change. - */ - readonly executionSummary: NotebookCellExecutionSummary | undefined; - } - - /** - * Describes a structural change to a notebook document, e.g newly added and removed cells. - * - * @see {@link NotebookDocumentChangeEvent} - */ - export interface NotebookDocumentContentChange { - - /** - * The range at which cells have been either added or removed. - * - * Note that no cells have been {@link NotebookDocumentContentChange.removedCells removed} - * when this range is {@link NotebookRange.isEmpty empty}. - */ - readonly range: NotebookRange; - - /** - * Cells that have been added to the document. - */ - readonly addedCells: readonly NotebookCell[]; - - /** - * Cells that have been removed from the document. - */ - readonly removedCells: readonly NotebookCell[]; - } - - /** - * An event describing a transactional {@link NotebookDocument notebook} change. - */ - export interface NotebookDocumentChangeEvent { - - /** - * The affected notebook. - */ - readonly notebook: NotebookDocument; - - /** - * The new metadata of the notebook or `undefined` when it did not change. - */ - readonly metadata: { [key: string]: any } | undefined; - - /** - * An array of content changes describing added or removed {@link NotebookCell cells}. - */ - readonly contentChanges: readonly NotebookDocumentContentChange[]; - - /** - * An array of {@link NotebookDocumentCellChange cell changes}. - */ - readonly cellChanges: readonly NotebookDocumentCellChange[]; - } - - export namespace workspace { - - /** - * An event that is emitted when a {@link NotebookDocument notebook} is saved. - */ - export const onDidSaveNotebookDocument: Event; - - /** - * An event that is emitted when a {@link NotebookDocument notebook} has changed. - */ - export const onDidChangeNotebookDocument: Event; - } -} From 26ea91e8ce971573289d366b8f95689cda591f26 Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 20 Apr 2022 13:58:31 +0200 Subject: [PATCH 170/245] update distro, https://github.com/microsoft/vscode/issues/144662 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cf2af1650a3..2fc5a5bea62 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.67.0", - "distro": "0db75775b5bb0ca7b9f311350da4a3c1913cb4f7", + "distro": "6cb70cc8b7fd45faf408753b1ff26f59e62112aa", "author": { "name": "Microsoft Corporation" }, From a045f3f48a5efea398eabf4e0b3a7dd92824dabc Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 20 Apr 2022 15:06:31 +0300 Subject: [PATCH 171/245] Send all extension descriptions to all extension hosts (#145307) --- .../api/browser/mainThreadExtensionService.ts | 9 +- .../workbench/api/common/extHost.protocol.ts | 6 +- .../api/common/extHostExtensionService.ts | 109 +++++++++---- .../workbench/api/common/extensionHostMain.ts | 2 +- .../api/node/extHostExtensionService.ts | 2 +- .../api/worker/extHostExtensionService.ts | 2 +- .../extensions/browser/extensionService.ts | 8 +- .../browser/webWorkerExtensionHost.ts | 20 ++- .../common/abstractExtensionService.ts | 10 +- .../common/extensionDescriptionRegistry.ts | 6 +- .../extensions/common/extensionHostManager.ts | 51 ++++--- .../common/extensionHostProtocol.ts | 12 +- .../extensions/common/extensionHostProxy.ts | 7 +- .../services/extensions/common/extensions.ts | 143 +++++++++++++++++- .../extensions/common/remoteExtensionHost.ts | 25 +-- .../electron-browser/extensionService.ts | 14 +- .../localProcessExtensionHost.ts | 17 +-- 17 files changed, 311 insertions(+), 132 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadExtensionService.ts b/src/vs/workbench/api/browser/mainThreadExtensionService.ts index 1b9316170ad..41309ab2bd9 100644 --- a/src/vs/workbench/api/browser/mainThreadExtensionService.ts +++ b/src/vs/workbench/api/browser/mainThreadExtensionService.ts @@ -26,6 +26,7 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { URI, UriComponents } from 'vs/base/common/uri'; import { FileAccess } from 'vs/base/common/network'; +import { IExtensionDescriptionDelta } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; @extHostNamedCustomer(MainContext.MainThreadExtensionService) export class MainThreadExtensionService implements MainThreadExtensionServiceShape { @@ -204,8 +205,8 @@ class ExtensionHostProxy implements IExtensionHostProxy { const uriComponents = await this._actual.$getCanonicalURI(remoteAuthority, uri); return (uriComponents ? URI.revive(uriComponents) : uriComponents); } - startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise { - return this._actual.$startExtensionHost(enabledExtensionIds); + startExtensionHost(extensionsDelta: IExtensionDescriptionDelta): Promise { + return this._actual.$startExtensionHost(extensionsDelta); } extensionTestsExecute(): Promise { return this._actual.$extensionTestsExecute(); @@ -225,8 +226,8 @@ class ExtensionHostProxy implements IExtensionHostProxy { updateRemoteConnectionData(connectionData: IRemoteConnectionData): Promise { return this._actual.$updateRemoteConnectionData(connectionData); } - deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise { - return this._actual.$deltaExtensions(toAdd, toRemove); + deltaExtensions(extensionsDelta: IExtensionDescriptionDelta): Promise { + return this._actual.$deltaExtensions(extensionsDelta); } test_latency(n: number): Promise { return this._actual.$test_latency(n); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 8ccdce38e97..4787ccb401b 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -62,7 +62,7 @@ import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescrip import { TypeHierarchyItem } from 'vs/workbench/contrib/typeHierarchy/common/typeHierarchy'; import { AuthenticationProviderInformation, AuthenticationSession, AuthenticationSessionsChangeEvent } from 'vs/workbench/services/authentication/common/authentication'; import { EditorGroupColumn } from 'vs/workbench/services/editor/common/editorGroupColumn'; -import { IStaticWorkspaceData } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; +import { IExtensionDescriptionDelta, IStaticWorkspaceData } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { IResolveAuthorityResult } from 'vs/workbench/services/extensions/common/extensionHostProxy'; import { ActivationKind, ExtensionActivationReason, MissingExtensionDependency } from 'vs/workbench/services/extensions/common/extensions'; import { createProxyIdentifier, Dto, IRPCProtocol, SerializableObjectWithBuffers } from 'vs/workbench/services/extensions/common/proxyIdentifier'; @@ -1434,7 +1434,7 @@ export interface ExtHostExtensionServiceShape { * Returns `null` if no resolver for `remoteAuthority` is found. */ $getCanonicalURI(remoteAuthority: string, uri: UriComponents): Promise; - $startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise; + $startExtensionHost(extensionsDelta: IExtensionDescriptionDelta): Promise; $extensionTestsExecute(): Promise; $extensionTestsExit(code: number): Promise; $activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise; @@ -1442,7 +1442,7 @@ export interface ExtHostExtensionServiceShape { $setRemoteEnvironment(env: { [key: string]: string | null }): Promise; $updateRemoteConnectionData(connectionData: IRemoteConnectionData): Promise; - $deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise; + $deltaExtensions(extensionsDelta: IExtensionDescriptionDelta): Promise; $test_latency(n: number): Promise; $test_up(b: VSBuffer): Promise; diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index a60f54b98be..71ab9fb23aa 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -13,12 +13,12 @@ import { TernarySearchTree } from 'vs/base/common/map'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ILogService } from 'vs/platform/log/common/log'; import { ExtHostExtensionServiceShape, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape } from 'vs/workbench/api/common/extHost.protocol'; -import { IExtensionHostInitData } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; +import { IExtensionDescriptionDelta, IExtensionHostInitData } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { ExtHostConfiguration, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; import { ActivatedExtension, EmptyExtension, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionModule, HostExtension, ExtensionActivationTimesFragment } from 'vs/workbench/api/common/extHostExtensionActivator'; import { ExtHostStorage, IExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; -import { MissingExtensionDependency, ActivationKind, checkProposedApiEnabled, isProposedApiEnabled, ExtensionActivationReason } from 'vs/workbench/services/extensions/common/extensions'; +import { MissingExtensionDependency, ActivationKind, checkProposedApiEnabled, isProposedApiEnabled, ExtensionActivationReason, extensionIdentifiersArrayToSet } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import * as errors from 'vs/base/common/errors'; import type * as vscode from 'vscode'; @@ -100,7 +100,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme private readonly _readyToRunExtensions: Barrier; private readonly _eagerExtensionsActivated: Barrier; - protected readonly _registry: ExtensionDescriptionRegistry; + protected readonly _myRegistry: ExtensionDescriptionRegistry; + protected readonly _globalRegistry: ExtensionDescriptionRegistry; private readonly _storage: ExtHostStorage; private readonly _secretState: ExtHostSecretState; private readonly _storagePath: IExtensionStoragePaths; @@ -144,7 +145,11 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme this._readyToStartExtensionHost = new Barrier(); this._readyToRunExtensions = new Barrier(); this._eagerExtensionsActivated = new Barrier(); - this._registry = new ExtensionDescriptionRegistry(this._initData.extensions); + this._globalRegistry = new ExtensionDescriptionRegistry(this._initData.allExtensions); + const myExtensionsSet = extensionIdentifiersArrayToSet(this._initData.myExtensions); + this._myRegistry = new ExtensionDescriptionRegistry( + filterExtensions(this._globalRegistry, myExtensionsSet) + ); this._storage = new ExtHostStorage(this._extHostContext); this._secretState = new ExtHostSecretState(this._extHostContext); this._storagePath = storagePath; @@ -154,24 +159,33 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme [IExtHostSecretState, this._secretState] )); - const hostExtensions = new Set(); - this._initData.hostExtensions.forEach((extensionId) => hostExtensions.add(ExtensionIdentifier.toKey(extensionId))); + let resolvedExtensions: ExtensionIdentifier[] = []; + let hostExtensions: ExtensionIdentifier[] = []; + if (this._initData.remote.isRemote) { + resolvedExtensions = this._initData.allExtensions.filter(extension => !extension.main && !extension.browser).map(extension => extension.identifier); + hostExtensions = ( + this._initData.allExtensions + .filter(extension => !myExtensionsSet.has(ExtensionIdentifier.toKey(extension.identifier.value))) + .filter(extension => (extension.main || extension.browser) && extension.api === 'none').map(extension => extension.identifier) + ); + } + const hostExtensionsSet = extensionIdentifiersArrayToSet(hostExtensions); this._activator = this._register(new ExtensionsActivator( - this._registry, - this._initData.resolvedExtensions, - this._initData.hostExtensions, + this._myRegistry, + resolvedExtensions, + hostExtensions, { onExtensionActivationError: (extensionId: ExtensionIdentifier, error: Error, missingExtensionDependency: MissingExtensionDependency | null): void => { this._mainThreadExtensionsProxy.$onExtensionActivationError(extensionId, errors.transformErrorForSerialization(error), missingExtensionDependency); }, actualActivateExtension: async (extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise => { - if (hostExtensions.has(ExtensionIdentifier.toKey(extensionId))) { + if (hostExtensionsSet.has(ExtensionIdentifier.toKey(extensionId))) { await this._mainThreadExtensionsProxy.$activateExtension(extensionId, reason); return new HostExtension(); } - const extensionDescription = this._registry.getExtensionDescription(extensionId)!; + const extensionDescription = this._myRegistry.getExtensionDescription(extensionId)!; return this._activateExtension(extensionDescription, reason); } }, @@ -210,7 +224,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme let allPromises: Promise[] = []; try { - const allExtensions = this._registry.getAllExtensionDescriptions(); + const allExtensions = this._myRegistry.getAllExtensionDescriptions(); const allExtensionsIds = allExtensions.map(ext => ext.identifier); const activatedExtensions = allExtensionsIds.filter(id => this.isActivated(id)); @@ -293,7 +307,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme } public getExtensionRegistry(): Promise { - return this._readyToRunExtensions.wait().then(_ => this._registry); + return this._readyToRunExtensions.wait().then(_ => this._myRegistry); } public getExtensionExports(extensionId: ExtensionIdentifier): IExtensionAPI | null | undefined { @@ -326,7 +340,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme return extUriBiasedIgnorePathCase.ignorePathCasing(key); }); // const tst = TernarySearchTree.forUris(key => true); - for (const ext of this._registry.getAllExtensionDescriptions()) { + for (const ext of this._myRegistry.getAllExtensionDescriptions()) { if (this._getEntryPoint(ext)) { const uri = await this._realPathExtensionUri(ext.extensionLocation); tst.set(uri, ext); @@ -572,7 +586,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme // startup is considered finished this._mainThreadExtensionsProxy.$setPerformanceMarks(performance.getMarks()); - for (const desc of this._registry.getAllExtensionDescriptions()) { + for (const desc of this._myRegistry.getAllExtensionDescriptions()) { if (desc.activationEvents) { for (const activationEvent of desc.activationEvents) { if (activationEvent === 'onStartupFinished') { @@ -607,7 +621,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme } return Promise.all( - this._registry.getAllExtensionDescriptions().map((desc) => { + this._myRegistry.getAllExtensionDescriptions().map((desc) => { return this._handleWorkspaceContainsEagerExtension(folders, desc); }) ).then(() => { }); @@ -816,8 +830,19 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme return result; } - public $startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise { - this._registry.keepOnly(enabledExtensionIds); + public $startExtensionHost(extensionsDelta: IExtensionDescriptionDelta): Promise { + extensionsDelta.toAdd.forEach((extension) => (extension).extensionLocation = URI.revive(extension.extensionLocation)); + this._globalRegistry.deltaExtensions(extensionsDelta.toAdd, extensionsDelta.toRemove); + + const myExtensions = extensionIdentifiersArrayToSet(this._myRegistry.getAllExtensionDescriptions().map(extension => extension.identifier)); + for (const extensionId of extensionsDelta.myToRemove) { + myExtensions.delete(ExtensionIdentifier.toKey(extensionId)); + } + for (const extensionId of extensionsDelta.myToAdd) { + myExtensions.add(ExtensionIdentifier.toKey(extensionId)); + } + this._myRegistry.set(filterExtensions(this._globalRegistry, myExtensions)); + return this._startExtensionHost(); } @@ -834,7 +859,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme public async $activate(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise { await this._readyToRunExtensions.wait(); - if (!this._registry.getExtensionDescription(extensionId)) { + if (!this._myRegistry.getExtensionDescription(extensionId)) { // unknown extension => ignore return false; } @@ -842,24 +867,36 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme return true; } - public async $deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise { - toAdd.forEach((extension) => (extension).extensionLocation = URI.revive(extension.extensionLocation)); + public async $deltaExtensions(extensionsDelta: IExtensionDescriptionDelta): Promise { + extensionsDelta.toAdd.forEach((extension) => (extension).extensionLocation = URI.revive(extension.extensionLocation)); - const trie = await this.getExtensionPathIndex(); + this._globalRegistry.deltaExtensions(extensionsDelta.toAdd, extensionsDelta.toRemove); - await Promise.all(toRemove.map(async (extensionId) => { - const extensionDescription = this._registry.getExtensionDescription(extensionId); - if (extensionDescription) { - trie.delete(await this._realPathExtensionUri(extensionDescription.extensionLocation)); - } - })); + const myExtensions = extensionIdentifiersArrayToSet(this._myRegistry.getAllExtensionDescriptions().map(extension => extension.identifier)); + for (const extensionId of extensionsDelta.myToRemove) { + myExtensions.delete(ExtensionIdentifier.toKey(extensionId)); + } + for (const extensionId of extensionsDelta.myToAdd) { + myExtensions.add(ExtensionIdentifier.toKey(extensionId)); + } + this._myRegistry.set(filterExtensions(this._globalRegistry, myExtensions)); - await Promise.all(toAdd.map(async (extensionDescription) => { - const realpathUri = await this._realPathExtensionUri(extensionDescription.extensionLocation); - trie.set(realpathUri, extensionDescription); - })); + console.log(`TODO: update the extension path trie!`); + + // const trie = await this.getExtensionPathIndex(); + + // await Promise.all(toRemove.map(async (extensionId) => { + // const extensionDescription = this._myRegistry.getExtensionDescription(extensionId); + // if (extensionDescription) { + // trie.delete(await this._realPathExtensionUri(extensionDescription.extensionLocation)); + // } + // })); + + // await Promise.all(toAdd.map(async (extensionDescription) => { + // const realpathUri = await this._realPathExtensionUri(extensionDescription.extensionLocation); + // trie.set(realpathUri, extensionDescription); + // })); - this._registry.deltaExtensions(toAdd, toRemove); return Promise.resolve(undefined); } @@ -976,6 +1013,12 @@ export class Extension implements vscode.Ex } } +function filterExtensions(globalRegistry: ExtensionDescriptionRegistry, desiredExtensions: Set): IExtensionDescription[] { + return globalRegistry.getAllExtensionDescriptions().filter( + extension => desiredExtensions.has(ExtensionIdentifier.toKey(extension.identifier)) + ); +} + function getRemoteAuthorityPrefix(remoteAuthority: string): string { const plusIndex = remoteAuthority.indexOf('+'); if (plusIndex === -1) { diff --git a/src/vs/workbench/api/common/extensionHostMain.ts b/src/vs/workbench/api/common/extensionHostMain.ts index 99c7c7b08fd..0eabc9e29cc 100644 --- a/src/vs/workbench/api/common/extensionHostMain.ts +++ b/src/vs/workbench/api/common/extensionHostMain.ts @@ -120,7 +120,7 @@ export class ExtensionHostMain { } private static _transform(initData: IExtensionHostInitData, rpcProtocol: RPCProtocol): IExtensionHostInitData { - initData.extensions.forEach((ext) => (ext).extensionLocation = URI.revive(rpcProtocol.transformIncomingURIs(ext.extensionLocation))); + initData.allExtensions.forEach((ext) => (ext).extensionLocation = URI.revive(rpcProtocol.transformIncomingURIs(ext.extensionLocation))); initData.environment.appRoot = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.appRoot)); const extDevLocs = initData.environment.extensionDevelopmentLocationURI; if (extDevLocs) { diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 481409b79d5..8521f23ca0a 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -72,7 +72,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { } // Module loading tricks - const interceptor = this._instaService.createInstance(NodeModuleRequireInterceptor, extensionApiFactory, this._registry); + const interceptor = this._instaService.createInstance(NodeModuleRequireInterceptor, extensionApiFactory, this._myRegistry); await interceptor.install(); performance.mark('code/extHost/didInitAPI'); diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts index 68c7c834910..cc072dcdacd 100644 --- a/src/vs/workbench/api/worker/extHostExtensionService.ts +++ b/src/vs/workbench/api/worker/extHostExtensionService.ts @@ -44,7 +44,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { // initialize API and register actors const apiFactory = this._instaService.invokeFunction(createApiFactoryAndRegisterActors); - this._fakeModules = this._instaService.createInstance(WorkerRequireInterceptor, apiFactory, this._registry); + this._fakeModules = this._instaService.createInstance(WorkerRequireInterceptor, apiFactory, this._myRegistry); await this._fakeModules.install(); performance.mark('code/extHost/didInitAPI'); diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index 42a05f2344a..8ebc2b75496 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -112,8 +112,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten const allExtensions = await this.getExtensions(); const localWebWorkerExtensions = this._filterByRunningLocation(allExtensions, desiredRunningLocation); return { - autoStart: true, - extensions: localWebWorkerExtensions + allExtensions: allExtensions, + myExtensions: localWebWorkerExtensions.map(extension => extension.identifier) }; } }; @@ -232,8 +232,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten extensionHostLogsPath: remoteEnv.extensionHostLogsPath, globalStorageHome: remoteEnv.globalStorageHome, workspaceStorageHome: remoteEnv.workspaceStorageHome, - extensions: remoteExtensions, - allExtensions: this._registry.getAllExtensionDescriptions() + allExtensions: this._registry.getAllExtensionDescriptions(), + myExtensions: remoteExtensions.map(extension => extension.identifier), }; } diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts index 85bff25d857..e67094778e7 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts @@ -12,11 +12,11 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { ILabelService } from 'vs/platform/label/common/label'; import { ILogService } from 'vs/platform/log/common/log'; -import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import * as platform from 'vs/base/common/platform'; import * as dom from 'vs/base/browser/dom'; import { URI } from 'vs/base/common/uri'; -import { IExtensionHost, ExtensionHostLogFileName, LocalWebWorkerRunningLocation } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionHost, ExtensionHostLogFileName, LocalWebWorkerRunningLocation, ExtensionHostExtensions } from 'vs/workbench/services/extensions/common/extensions'; import { IProductService } from 'vs/platform/product/common/productService'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { joinPath } from 'vs/base/common/resources'; @@ -30,11 +30,10 @@ import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { FileAccess } from 'vs/base/common/network'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { parentOriginHash } from 'vs/workbench/browser/webview'; -import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; export interface IWebWorkerExtensionHostInitData { - readonly autoStart: boolean; - readonly extensions: IExtensionDescription[]; + readonly allExtensions: IExtensionDescription[]; + readonly myExtensions: ExtensionIdentifier[]; } export interface IWebWorkerExtensionHostDataProvider { @@ -45,7 +44,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost public readonly remoteAuthority = null; public readonly lazyStart: boolean; - public readonly extensions = new ExtensionDescriptionRegistry([]); + public readonly extensions = new ExtensionHostExtensions(); private readonly _onDidExit = this._register(new Emitter<[number, string | null]>()); public readonly onExit: Event<[number, string | null]> = this._onDidExit.event; @@ -267,7 +266,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost private async _createExtHostInitData(): Promise { const [telemetryInfo, initData] = await Promise.all([this._telemetryService.getTelemetryInfo(), this._initDataProvider.getInitData()]); const workspace = this._contextService.getWorkspace(); - this.extensions.deltaExtensions(initData.extensions, []); + const deltaExtensions = this.extensions.set(initData.allExtensions, initData.myExtensions); return { commit: this._productService.commit, version: this._productService.version, @@ -289,14 +288,13 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost name: this._labelService.getWorkspaceLabel(workspace), transient: workspace.transient }, - resolvedExtensions: [], - hostExtensions: [], - extensions: this.extensions.getAllExtensionDescriptions(), + allExtensions: deltaExtensions.toAdd, + myExtensions: deltaExtensions.myToAdd, telemetryInfo, logLevel: this._logService.getLevel(), logsLocation: this._extensionHostLogsLocation, logFile: this._extensionHostLogFile, - autoStart: initData.autoStart, + autoStart: true, remote: { authority: this._environmentService.remoteAuthority, connectionData: null, diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index 0334f7f0b99..fc7e02bdcfc 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -628,12 +628,10 @@ export abstract class AbstractExtensionService extends Disposable implements IEx await Promise.all(promises); } - private async _updateExtensionsOnExtHost(extensionHostManager: IExtensionHostManager, _toAdd: IExtensionDescription[], _toRemove: ExtensionIdentifier[], removedRunningLocation: Map): Promise { - const toAdd = filterByExtensionHostManager(_toAdd, this._runningLocation, extensionHostManager); - const toRemove = _filterByExtensionHostManager(_toRemove, extId => extId, removedRunningLocation, extensionHostManager); - if (toRemove.length > 0 || toAdd.length > 0) { - await extensionHostManager.deltaExtensions(toAdd, toRemove); - } + private async _updateExtensionsOnExtHost(extensionHostManager: IExtensionHostManager, toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[], removedRunningLocation: Map): Promise { + const myToAdd = filterByExtensionHostManager(toAdd, this._runningLocation, extensionHostManager); + const myToRemove = _filterByExtensionHostManager(toRemove, extId => extId, removedRunningLocation, extensionHostManager); + await extensionHostManager.deltaExtensions({ toRemove, toAdd, myToRemove, myToAdd: myToAdd.map(extension => extension.identifier) }); } public canAddExtension(extension: IExtensionDescription): boolean { diff --git a/src/vs/workbench/services/extensions/common/extensionDescriptionRegistry.ts b/src/vs/workbench/services/extensions/common/extensionDescriptionRegistry.ts index b3cb9b17813..274bc9c396e 100644 --- a/src/vs/workbench/services/extensions/common/extensionDescriptionRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionDescriptionRegistry.ts @@ -61,10 +61,8 @@ export class ExtensionDescriptionRegistry { } } - public keepOnly(extensionIds: ExtensionIdentifier[]): void { - const toKeep = new Set(); - extensionIds.forEach(extensionId => toKeep.add(ExtensionIdentifier.toKey(extensionId))); - this._extensionDescriptions = this._extensionDescriptions.filter(extension => toKeep.has(ExtensionIdentifier.toKey(extension.identifier))); + public set(extensionDescriptions: IExtensionDescription[]) { + this._extensionDescriptions = extensionDescriptions; this._initialize(); this._onDidChange.fire(undefined); } diff --git a/src/vs/workbench/services/extensions/common/extensionHostManager.ts b/src/vs/workbench/services/extensions/common/extensionHostManager.ts index e0aff464773..eb4c4437b6a 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostManager.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostManager.ts @@ -19,13 +19,14 @@ import { registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { StopWatch } from 'vs/base/common/stopwatch'; import { VSBuffer } from 'vs/base/common/buffer'; -import { IExtensionHost, ExtensionHostKind, ActivationKind, extensionHostKindToString, ExtensionActivationReason, IInternalExtensionService, ExtensionRunningLocation } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionHost, ExtensionHostKind, ActivationKind, extensionHostKindToString, ExtensionActivationReason, IInternalExtensionService, ExtensionRunningLocation, ExtensionHostExtensions } from 'vs/workbench/services/extensions/common/extensions'; import { CATEGORIES } from 'vs/workbench/common/actions'; import { Barrier, timeout } from 'vs/base/common/async'; import { URI } from 'vs/base/common/uri'; import { ILogService } from 'vs/platform/log/common/log'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtensionHostProxy, IResolveAuthorityResult } from 'vs/workbench/services/extensions/common/extensionHostProxy'; +import { IExtensionDescriptionDelta } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; // Enable to see detailed message communication between window and extension host const LOG_EXTENSION_HOST_COMMUNICATION = false; @@ -39,7 +40,7 @@ export interface IExtensionHostManager { dispose(): void; ready(): Promise; representsRunningLocation(runningLocation: ExtensionRunningLocation): boolean; - deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise; + deltaExtensions(extensionsDelta: IExtensionDescriptionDelta): Promise; containsExtension(extensionId: ExtensionIdentifier): boolean; activate(extension: ExtensionIdentifier, reason: ExtensionActivationReason): Promise; activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise; @@ -50,7 +51,7 @@ export interface IExtensionHostManager { * Returns `null` if no resolver for `remoteAuthority` is found. */ getCanonicalURI(remoteAuthority: string, uri: URI): Promise; - start(enabledExtensionIds: ExtensionIdentifier[]): Promise; + start(allExtensions: IExtensionDescription[], myExtensions: ExtensionIdentifier[]): Promise; extensionTestsExecute(): Promise; extensionTestsSendExit(exitCode: number): Promise; setRemoteEnvironment(env: { [key: string]: string | null }): Promise; @@ -398,13 +399,13 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager { return proxy.getCanonicalURI(remoteAuthority, uri); } - public async start(enabledExtensionIds: ExtensionIdentifier[]): Promise { + public async start(allExtensions: IExtensionDescription[], myExtensions: ExtensionIdentifier[]): Promise { const proxy = await this._proxy; if (!proxy) { return; } - this._extensionHost.extensions.keepOnly(enabledExtensionIds); - return proxy.startExtensionHost(enabledExtensionIds); + const deltaExtensions = this._extensionHost.extensions.set(allExtensions, myExtensions); + return proxy.startExtensionHost(deltaExtensions); } public async extensionTestsExecute(): Promise { @@ -433,13 +434,13 @@ class ExtensionHostManager extends Disposable implements IExtensionHostManager { return this._extensionHost.runningLocation.equals(runningLocation); } - public async deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise { + public async deltaExtensions(extensionsDelta: IExtensionDescriptionDelta): Promise { const proxy = await this._proxy; if (!proxy) { return; } - this._extensionHost.extensions.deltaExtensions(toAdd, toRemove); - return proxy.deltaExtensions(toAdd, toRemove); + this._extensionHost.extensions.delta(extensionsDelta); + return proxy.deltaExtensions(extensionsDelta); } public containsExtension(extensionId: ExtensionIdentifier): boolean { @@ -468,6 +469,7 @@ class LazyStartExtensionHostManager extends Disposable implements IExtensionHost private readonly _extensionHost: IExtensionHost; private _startCalled: Barrier; private _actual: ExtensionHostManager | null; + private _lazyStartExtensions: ExtensionHostExtensions | null; public get kind(): ExtensionHostKind { return this._extensionHost.runningLocation.kind; @@ -485,6 +487,7 @@ class LazyStartExtensionHostManager extends Disposable implements IExtensionHost this.onDidExit = extensionHost.onExit; this._startCalled = new Barrier(); this._actual = null; + this._lazyStartExtensions = null; } private _createActual(reason: string): ExtensionHostManager { @@ -500,7 +503,7 @@ class LazyStartExtensionHostManager extends Disposable implements IExtensionHost return this._actual; } const actual = this._createActual(reason); - await actual.start([]); + await actual.start([], []); return actual; } @@ -513,13 +516,17 @@ class LazyStartExtensionHostManager extends Disposable implements IExtensionHost public representsRunningLocation(runningLocation: ExtensionRunningLocation): boolean { return this._extensionHost.runningLocation.equals(runningLocation); } - public async deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise { + public async deltaExtensions(extensionsDelta: IExtensionDescriptionDelta): Promise { await this._startCalled.wait(); - const extensionHostAlreadyStarted = Boolean(this._actual); - const shouldStartExtensionHost = (toAdd.length > 0); - if (extensionHostAlreadyStarted || shouldStartExtensionHost) { - const actual = await this._getOrCreateActualAndStart(`contains ${toAdd.length} new extension(s) (installed or enabled): ${toAdd.map(ext => ext.identifier.value)}`); - return actual.deltaExtensions(toAdd, toRemove); + if (this._actual) { + return this._actual.deltaExtensions(extensionsDelta); + } + this._lazyStartExtensions!.delta(extensionsDelta); + if (extensionsDelta.myToAdd.length > 0) { + const actual = this._createActual(`contains ${extensionsDelta.myToAdd.length} new extension(s) (installed or enabled): ${extensionsDelta.myToAdd.map(extId => extId.value)}`); + const { toAdd, myToAdd } = this._lazyStartExtensions!.toDelta(); + actual.start(toAdd, myToAdd); + return; } } public containsExtension(extensionId: ExtensionIdentifier): boolean { @@ -582,15 +589,17 @@ class LazyStartExtensionHostManager extends Disposable implements IExtensionHost } throw new Error(`Cannot resolve canonical URI`); } - public async start(enabledExtensionIds: ExtensionIdentifier[]): Promise { - if (enabledExtensionIds.length > 0) { + public async start(allExtensions: IExtensionDescription[], myExtensions: ExtensionIdentifier[]): Promise { + if (myExtensions.length > 0) { // there are actual extensions, so let's launch the extension host - const actual = this._createActual(`contains ${enabledExtensionIds.length} extension(s): ${enabledExtensionIds.map(extId => extId.value)}.`); - const result = actual.start(enabledExtensionIds); + const actual = this._createActual(`contains ${myExtensions.length} extension(s): ${myExtensions.map(extId => extId.value)}.`); + const result = actual.start(allExtensions, myExtensions); this._startCalled.open(); return result; } - // there are no actual extensions + // there are no actual extensions running, store extensions in `this._lazyStartExtensions` + this._lazyStartExtensions = new ExtensionHostExtensions(); + this._lazyStartExtensions.set(allExtensions, myExtensions); this._startCalled.open(); } public async extensionTestsExecute(): Promise { diff --git a/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts b/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts index a090dced1df..ca72df6f92f 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts @@ -10,15 +10,21 @@ import { LogLevel } from 'vs/platform/log/common/log'; import { IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; +export interface IExtensionDescriptionDelta { + readonly toRemove: ExtensionIdentifier[]; + readonly toAdd: IExtensionDescription[]; + readonly myToRemove: ExtensionIdentifier[]; + readonly myToAdd: ExtensionIdentifier[]; +} + export interface IExtensionHostInitData { version: string; commit?: string; parentPid: number; environment: IEnvironment; workspace?: IStaticWorkspaceData | null; - resolvedExtensions: ExtensionIdentifier[]; - hostExtensions: ExtensionIdentifier[]; - extensions: IExtensionDescription[]; + allExtensions: IExtensionDescription[]; + myExtensions: ExtensionIdentifier[]; telemetryInfo: ITelemetryInfo; logLevel: LogLevel; logsLocation: URI; diff --git a/src/vs/workbench/services/extensions/common/extensionHostProxy.ts b/src/vs/workbench/services/extensions/common/extensionHostProxy.ts index d021dda0f0d..b528989a727 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostProxy.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostProxy.ts @@ -5,8 +5,9 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { URI } from 'vs/base/common/uri'; -import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IRemoteConnectionData, RemoteAuthorityResolverErrorCode, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { IExtensionDescriptionDelta } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { ActivationKind, ExtensionActivationReason } from 'vs/workbench/services/extensions/common/extensions'; export interface IResolveAuthorityErrorResult { @@ -31,14 +32,14 @@ export interface IExtensionHostProxy { * Returns `null` if no resolver for `remoteAuthority` is found. */ getCanonicalURI(remoteAuthority: string, uri: URI): Promise; - startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise; + startExtensionHost(extensionsDelta: IExtensionDescriptionDelta): Promise; extensionTestsExecute(): Promise; extensionTestsExit(code: number): Promise; activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise; activate(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise; setRemoteEnvironment(env: { [key: string]: string | null }): Promise; updateRemoteConnectionData(connectionData: IRemoteConnectionData): Promise; - deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise; + deltaExtensions(extensionsDelta: IExtensionDescriptionDelta): Promise; test_latency(n: number): Promise; test_up(b: VSBuffer): Promise; test_down(size: number): Promise; diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index db9df68c5f6..1a564e9a491 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -13,7 +13,7 @@ import { getExtensionId, getGalleryExtensionId } from 'vs/platform/extensionMana import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { ApiProposalName } from 'vs/workbench/services/extensions/common/extensionsApiProposals'; import { IV8Profile } from 'vs/platform/profiling/common/profiling'; -import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; +import { IExtensionDescriptionDelta } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; export const nullExtensionDescription = Object.freeze({ identifier: new ExtensionIdentifier('nullExtensionDescription'), @@ -150,10 +150,11 @@ export interface IExtensionHost { readonly remoteAuthority: string | null; readonly lazyStart: boolean; /** - * A collection of extensions that will execute or are executing on this extension host. + * A collection of extensions which includes information about which + * extension will execute or is executing on this extension host. * **NOTE**: this will reflect extensions correctly only after `start()` resolves. */ - readonly extensions: ExtensionDescriptionRegistry; + readonly extensions: ExtensionHostExtensions; readonly onExit: Event<[number, string | null]>; start(): Promise | null; @@ -162,6 +163,142 @@ export interface IExtensionHost { dispose(): void; } +export class ExtensionHostExtensions { + + private _allExtensions: IExtensionDescription[]; + private _myExtensions: ExtensionIdentifier[]; + + constructor() { + this._allExtensions = []; + this._myExtensions = []; + } + + public toDelta(): IExtensionDescriptionDelta { + return { + toRemove: [], + toAdd: this._allExtensions, + myToRemove: [], + myToAdd: this._myExtensions + }; + } + + public set(allExtensions: IExtensionDescription[], myExtensions: ExtensionIdentifier[]): IExtensionDescriptionDelta { + const toRemove: ExtensionIdentifier[] = []; + const toAdd: IExtensionDescription[] = []; + const myToRemove: ExtensionIdentifier[] = []; + const myToAdd: ExtensionIdentifier[] = []; + + const oldExtensionsMap = extensionDescriptionArrayToMap(this._allExtensions); + const newExtensionsMap = extensionDescriptionArrayToMap(allExtensions); + const extensionsAreTheSame = (a: IExtensionDescription, b: IExtensionDescription) => { + return ( + (a.extensionLocation.toString() === b.extensionLocation.toString()) + || (a.isBuiltin === b.isBuiltin) + || (a.isUserBuiltin === b.isUserBuiltin) + || (a.isUnderDevelopment === b.isUnderDevelopment) + ); + }; + + for (const oldExtension of this._allExtensions) { + const newExtension = newExtensionsMap.get(ExtensionIdentifier.toKey(oldExtension.identifier)); + if (!newExtension) { + toRemove.push(oldExtension.identifier); + oldExtensionsMap.delete(ExtensionIdentifier.toKey(oldExtension.identifier)); + continue; + } + if (!extensionsAreTheSame(oldExtension, newExtension)) { + // The new extension is different than the old one + // (e.g. maybe it executes in a different location) + toRemove.push(oldExtension.identifier); + oldExtensionsMap.delete(ExtensionIdentifier.toKey(oldExtension.identifier)); + continue; + } + } + for (const newExtension of allExtensions) { + const oldExtension = oldExtensionsMap.get(ExtensionIdentifier.toKey(newExtension.identifier)); + if (!oldExtension) { + toAdd.push(newExtension); + continue; + } + if (!extensionsAreTheSame(oldExtension, newExtension)) { + // The new extension is different than the old one + // (e.g. maybe it executes in a different location) + toRemove.push(oldExtension.identifier); + oldExtensionsMap.delete(ExtensionIdentifier.toKey(oldExtension.identifier)); + continue; + } + } + + const myOldExtensionsSet = extensionIdentifiersArrayToSet(this._myExtensions); + const myNewExtensionsSet = extensionIdentifiersArrayToSet(myExtensions); + for (const oldExtensionId of this._myExtensions) { + if (!myNewExtensionsSet.has(ExtensionIdentifier.toKey(oldExtensionId))) { + myToRemove.push(oldExtensionId); + } + } + for (const newExtensionId of myExtensions) { + if (!myOldExtensionsSet.has(ExtensionIdentifier.toKey(newExtensionId))) { + myToAdd.push(newExtensionId); + } + } + + const delta = { toRemove, toAdd, myToRemove, myToAdd }; + this.delta(delta); + return delta; + } + + public delta(extensionsDelta: IExtensionDescriptionDelta): void { + const { toRemove, toAdd, myToRemove, myToAdd } = extensionsDelta; + // First handle removals + const toRemoveSet = extensionIdentifiersArrayToSet(toRemove); + const myToRemoveSet = extensionIdentifiersArrayToSet(myToRemove); + for (let i = 0; i < this._allExtensions.length; i++) { + if (toRemoveSet.has(ExtensionIdentifier.toKey(this._allExtensions[i].identifier))) { + this._allExtensions.splice(i, 1); + i--; + } + } + for (let i = 0; i < this._myExtensions.length; i++) { + if (myToRemoveSet.has(ExtensionIdentifier.toKey(this._myExtensions[i]))) { + this._myExtensions.splice(i, 1); + i--; + } + } + // Then handle additions + for (const extension of toAdd) { + this._allExtensions.push(extension); + } + for (const extensionId of myToAdd) { + this._myExtensions.push(extensionId); + } + } + + public containsExtension(extensionId: ExtensionIdentifier): boolean { + for (const myExtensionId of this._myExtensions) { + if (ExtensionIdentifier.equals(myExtensionId, extensionId)) { + return true; + } + } + return false; + } +} + +export function extensionIdentifiersArrayToSet(extensionIds: ExtensionIdentifier[]): Set { + const result = new Set(); + for (const extensionId of extensionIds) { + result.add(ExtensionIdentifier.toKey(extensionId)); + } + return result; +} + +function extensionDescriptionArrayToMap(extensions: IExtensionDescription[]): Map { + const result = new Map(); + for (const extension of extensions) { + result.set(ExtensionIdentifier.toKey(extension.identifier), extension); + } + return result; +} + export function isProposedApiEnabled(extension: IExtensionDescription, proposal: ApiProposalName): boolean { if (!extension.enabledApiProposals) { return false; diff --git a/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts b/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts index bd382d7f168..b6eb37647f8 100644 --- a/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts +++ b/src/vs/workbench/services/extensions/common/remoteExtensionHost.ts @@ -25,10 +25,9 @@ import { ISignService } from 'vs/platform/sign/common/sign'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; import { createMessageOfType, isMessageOfType, MessageType, IExtensionHostInitData, UIKind } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; -import { ExtensionHostLogFileName, IExtensionHost, RemoteRunningLocation } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionHostExtensions, ExtensionHostLogFileName, IExtensionHost, RemoteRunningLocation } from 'vs/workbench/services/extensions/common/extensions'; import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Extensions, IOutputChannelRegistry } from 'vs/workbench/services/output/common/output'; @@ -39,8 +38,8 @@ export interface IRemoteExtensionHostInitData { readonly extensionHostLogsPath: URI; readonly globalStorageHome: URI; readonly workspaceStorageHome: URI; - readonly extensions: IExtensionDescription[]; readonly allExtensions: IExtensionDescription[]; + readonly myExtensions: ExtensionIdentifier[]; } export interface IRemoteExtensionHostDataProvider { @@ -52,7 +51,7 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost { public readonly remoteAuthority: string; public readonly lazyStart = false; - public readonly extensions = new ExtensionDescriptionRegistry([]); + public readonly extensions = new ExtensionHostExtensions(); private _onExit: Emitter<[number, string | null]> = this._register(new Emitter<[number, string | null]>()); public readonly onExit: Event<[number, string | null]> = this._onExit.event; @@ -213,19 +212,8 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost { private async _createExtHostInitData(isExtensionDevelopmentDebug: boolean): Promise { const [telemetryInfo, remoteInitData] = await Promise.all([this._telemetryService.getTelemetryInfo(), this._initDataProvider.getInitData()]); - - // Collect all identifiers for extension ids which can be considered "resolved" - const remoteExtensions = new Set(); - remoteInitData.extensions.forEach((extension) => remoteExtensions.add(ExtensionIdentifier.toKey(extension.identifier.value))); - - const resolvedExtensions = remoteInitData.allExtensions.filter(extension => !extension.main && !extension.browser).map(extension => extension.identifier); - const hostExtensions = ( - remoteInitData.allExtensions - .filter(extension => !remoteExtensions.has(ExtensionIdentifier.toKey(extension.identifier.value))) - .filter(extension => (extension.main || extension.browser) && extension.api === 'none').map(extension => extension.identifier) - ); const workspace = this._contextService.getWorkspace(); - this.extensions.deltaExtensions(remoteInitData.extensions, []); + const deltaExtensions = this.extensions.set(remoteInitData.allExtensions, remoteInitData.myExtensions); return { commit: this._productService.commit, version: this._productService.version, @@ -253,9 +241,8 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost { authority: this._initDataProvider.remoteAuthority, connectionData: remoteInitData.connectionData }, - resolvedExtensions: resolvedExtensions, - hostExtensions: hostExtensions, - extensions: this.extensions.getAllExtensionDescriptions(), + allExtensions: deltaExtensions.toAdd, + myExtensions: deltaExtensions.myToAdd, telemetryInfo, logLevel: this._logService.getLevel(), logsLocation: remoteInitData.extensionHostLogsPath, diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 606ee940774..14be28cc18a 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -162,18 +162,20 @@ export class ExtensionService extends AbstractExtensionService implements IExten // Here we load even extensions that would be disabled by workspace trust const localExtensions = this._checkEnabledAndProposedAPI(await this._scanAllLocalExtensions(), /* ignore workspace trust */true); const runningLocation = this._determineRunningLocation(localExtensions); - const localProcessExtensions = filterByRunningLocation(localExtensions, runningLocation, desiredRunningLocation); + const myExtensions = filterByRunningLocation(localExtensions, runningLocation, desiredRunningLocation); return { autoStart: false, - extensions: localProcessExtensions + allExtensions: localExtensions, + myExtensions: myExtensions.map(extension => extension.identifier) }; } else { // restart case const allExtensions = await this.getExtensions(); - const localProcessExtensions = this._filterByRunningLocation(allExtensions, desiredRunningLocation); + const myExtensions = this._filterByRunningLocation(allExtensions, desiredRunningLocation); return { autoStart: true, - extensions: localProcessExtensions + allExtensions: allExtensions, + myExtensions: myExtensions.map(extension => extension.identifier) }; } } @@ -561,8 +563,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten extensionHostLogsPath: remoteEnv.extensionHostLogsPath, globalStorageHome: remoteEnv.globalStorageHome, workspaceStorageHome: remoteEnv.workspaceStorageHome, - extensions: remoteExtensions, allExtensions: this._registry.getAllExtensionDescriptions(), + myExtensions: remoteExtensions.map(extension => extension.identifier), }); } @@ -583,7 +585,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten private _startExtensionHost(extensionHostManager: IExtensionHostManager, _extensions: IExtensionDescription[]): void { const extensions = this._filterByExtensionHostManager(_extensions, extensionHostManager); - extensionHostManager.start(extensions.map(extension => extension.identifier)); + extensionHostManager.start(this._registry.getAllExtensionDescriptions(), extensions.map(extension => extension.identifier)); } public _onExtensionHostExit(code: number): void { diff --git a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts index 46a45aa5cf5..0f7f60bbc7d 100644 --- a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts @@ -29,11 +29,11 @@ import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; import { isUntitledWorkspace, IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { MessageType, createMessageOfType, isMessageOfType, IExtensionHostInitData, UIKind } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { withNullAsUndefined } from 'vs/base/common/types'; -import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { parseExtensionDevOptions } from '../common/extensionDevOptions'; import { VSBuffer } from 'vs/base/common/buffer'; import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug'; -import { IExtensionHost, ExtensionHostLogFileName, LocalProcessRunningLocation } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionHost, ExtensionHostLogFileName, LocalProcessRunningLocation, ExtensionHostExtensions } from 'vs/workbench/services/extensions/common/extensions'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { joinPath } from 'vs/base/common/resources'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -43,12 +43,12 @@ import { IExtensionHostProcessOptions, IExtensionHostStarter } from 'vs/platform import { SerializedError } from 'vs/base/common/errors'; import { removeDangerousEnvVariables } from 'vs/base/common/processes'; import { StopWatch } from 'vs/base/common/stopwatch'; -import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; export interface ILocalProcessExtensionHostInitData { readonly autoStart: boolean; - readonly extensions: IExtensionDescription[]; + readonly allExtensions: IExtensionDescription[]; + readonly myExtensions: ExtensionIdentifier[]; } export interface ILocalProcessExtensionHostDataProvider { @@ -108,7 +108,7 @@ export class LocalProcessExtensionHost implements IExtensionHost { public readonly remoteAuthority = null; public readonly lazyStart = false; - public readonly extensions = new ExtensionDescriptionRegistry([]); + public readonly extensions = new ExtensionHostExtensions(); private readonly _onExit: Emitter<[number, string]> = new Emitter<[number, string]>(); public readonly onExit: Event<[number, string]> = this._onExit.event; @@ -498,7 +498,7 @@ export class LocalProcessExtensionHost implements IExtensionHost { private async _createExtHostInitData(): Promise { const [telemetryInfo, initData] = await Promise.all([this._telemetryService.getTelemetryInfo(), this._initDataProvider.getInitData()]); const workspace = this._contextService.getWorkspace(); - this.extensions.deltaExtensions(initData.extensions, []); + const deltaExtensions = this.extensions.set(initData.allExtensions, initData.myExtensions); return { commit: this._productService.commit, version: this._productService.version, @@ -527,9 +527,8 @@ export class LocalProcessExtensionHost implements IExtensionHost { connectionData: null, isRemote: false }, - resolvedExtensions: [], - hostExtensions: [], - extensions: this.extensions.getAllExtensionDescriptions(), + allExtensions: deltaExtensions.toAdd, + myExtensions: deltaExtensions.myToAdd, telemetryInfo, logLevel: this._logService.getLevel(), logsLocation: this._environmentService.extHostLogsPath, From b1a6a7757d86439fffc966886147ac79e24bcd70 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 20 Apr 2022 14:54:44 +0200 Subject: [PATCH 172/245] Don't ask for inline completions for certain suggestions. --- .../browser/suggestWidgetPreviewModel.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetPreviewModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetPreviewModel.ts index 5a97c173751..0391d402bc1 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetPreviewModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetPreviewModel.ts @@ -8,7 +8,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; -import { InlineCompletionTriggerKind, SelectedSuggestionInfo } from 'vs/editor/common/languages'; +import { CompletionItemKind, InlineCompletionTriggerKind, SelectedSuggestionInfo } from 'vs/editor/common/languages'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { SharedInlineCompletionCache } from 'vs/editor/contrib/inlineCompletions/browser/ghostTextModel'; import { BaseGhostTextWidgetModel, GhostText } from './ghostText'; @@ -99,6 +99,17 @@ export class SuggestWidgetPreviewModel extends BaseGhostTextWidgetModel { const position = this.editor.getPosition(); + if ( + state.selectedItem.isSnippetText || + state.selectedItem.completionItemKind === CompletionItemKind.Snippet || + state.selectedItem.completionItemKind === CompletionItemKind.File || + state.selectedItem.completionItemKind === CompletionItemKind.Folder + ) { + // Don't ask providers for these types of suggestions. + this.cache.clear(); + return; + } + const promise = createCancelablePromise(async token => { let result: TrackedInlineCompletions; try { From b9e8734a7b99068e5bea687efb26c84b2faeb290 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 20 Apr 2022 16:37:56 +0300 Subject: [PATCH 173/245] Do fs calls in parallel --- .../api/common/extHostExtensionService.ts | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 71ab9fb23aa..312ef8d884a 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -332,26 +332,31 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme // create trie to enable fast 'filename -> extension id' look up public async getExtensionPathIndex(): Promise> { if (!this._extensionPathIndex) { - this._extensionPathIndex = (async () => { - const tst = TernarySearchTree.forUris(key => { - // using the default/biased extUri-util because the IExtHostFileSystemInfo-service - // isn't ready to be used yet, e.g the knowledge about `file` protocol and others - // comes in while this code runs - return extUriBiasedIgnorePathCase.ignorePathCasing(key); - }); - // const tst = TernarySearchTree.forUris(key => true); - for (const ext of this._myRegistry.getAllExtensionDescriptions()) { - if (this._getEntryPoint(ext)) { - const uri = await this._realPathExtensionUri(ext.extensionLocation); - tst.set(uri, ext); - } - } - return tst; - })(); + this._extensionPathIndex = this._createExtensionPathIndex(this._myRegistry.getAllExtensionDescriptions()); } return this._extensionPathIndex; } + /** + * create trie to enable fast 'filename -> extension id' look up + */ + private async _createExtensionPathIndex(extensions: IExtensionDescription[]): Promise> { + const tst = TernarySearchTree.forUris(key => { + // using the default/biased extUri-util because the IExtHostFileSystemInfo-service + // isn't ready to be used yet, e.g the knowledge about `file` protocol and others + // comes in while this code runs + return extUriBiasedIgnorePathCase.ignorePathCasing(key); + }); + // const tst = TernarySearchTree.forUris(key => true); + await Promise.all(this._myRegistry.getAllExtensionDescriptions().map(async (ext) => { + if (this._getEntryPoint(ext)) { + const uri = await this._realPathExtensionUri(ext.extensionLocation); + tst.set(uri, ext); + } + })); + return tst; + } + private _deactivate(extensionId: ExtensionIdentifier): Promise { let result = Promise.resolve(undefined); From 3e7fed03dc37734636bc673356f1c079d6154420 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 20 Apr 2022 17:00:44 +0300 Subject: [PATCH 174/245] Update the extension path tree after `$deltaExtensions` --- .../api/common/extHostExtensionService.ts | 91 +++++++++++-------- .../api/common/extHostRequireInterceptor.ts | 11 +-- 2 files changed, 59 insertions(+), 43 deletions(-) diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 312ef8d884a..f67924248f5 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -106,7 +106,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme private readonly _secretState: ExtHostSecretState; private readonly _storagePath: IExtensionStoragePaths; private readonly _activator: ExtensionsActivator; - private _extensionPathIndex: Promise> | null; + private _extensionPathIndex: Promise | null; private readonly _resolvers: { [authorityPrefix: string]: vscode.RemoteAuthorityResolver }; @@ -330,9 +330,11 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme } // create trie to enable fast 'filename -> extension id' look up - public async getExtensionPathIndex(): Promise> { + public async getExtensionPathIndex(): Promise { if (!this._extensionPathIndex) { - this._extensionPathIndex = this._createExtensionPathIndex(this._myRegistry.getAllExtensionDescriptions()); + this._extensionPathIndex = this._createExtensionPathIndex(this._myRegistry.getAllExtensionDescriptions()).then((searchTree) => { + return new ExtensionPaths(searchTree); + }); } return this._extensionPathIndex; } @@ -712,6 +714,11 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme } private _startExtensionHost(): Promise { + + console.log(`$startExtensionHost CALLED!`); + console.log(`myExtensions: ${this._myRegistry.getAllExtensionDescriptions().length} extensions`); + console.log(`allExtensions: ${this._globalRegistry.getAllExtensionDescriptions().length} extensions`); + if (this._started) { throw new Error(`Extension host is already started!`); } @@ -835,18 +842,28 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme return result; } - public $startExtensionHost(extensionsDelta: IExtensionDescriptionDelta): Promise { - extensionsDelta.toAdd.forEach((extension) => (extension).extensionLocation = URI.revive(extension.extensionLocation)); - this._globalRegistry.deltaExtensions(extensionsDelta.toAdd, extensionsDelta.toRemove); + private static _applyExtensionsDelta(oldGlobalRegistry: ExtensionDescriptionRegistry, oldMyRegistry: ExtensionDescriptionRegistry, extensionsDelta: IExtensionDescriptionDelta) { + const globalRegistry = new ExtensionDescriptionRegistry(oldGlobalRegistry.getAllExtensionDescriptions()); + globalRegistry.deltaExtensions(extensionsDelta.toAdd, extensionsDelta.toRemove); - const myExtensions = extensionIdentifiersArrayToSet(this._myRegistry.getAllExtensionDescriptions().map(extension => extension.identifier)); + const myExtensionsSet = extensionIdentifiersArrayToSet(oldMyRegistry.getAllExtensionDescriptions().map(extension => extension.identifier)); for (const extensionId of extensionsDelta.myToRemove) { - myExtensions.delete(ExtensionIdentifier.toKey(extensionId)); + myExtensionsSet.delete(ExtensionIdentifier.toKey(extensionId)); } for (const extensionId of extensionsDelta.myToAdd) { - myExtensions.add(ExtensionIdentifier.toKey(extensionId)); + myExtensionsSet.add(ExtensionIdentifier.toKey(extensionId)); } - this._myRegistry.set(filterExtensions(this._globalRegistry, myExtensions)); + const myExtensions = filterExtensions(globalRegistry, myExtensionsSet); + + return { globalRegistry, myExtensions }; + } + + public $startExtensionHost(extensionsDelta: IExtensionDescriptionDelta): Promise { + extensionsDelta.toAdd.forEach((extension) => (extension).extensionLocation = URI.revive(extension.extensionLocation)); + + const { globalRegistry, myExtensions } = AbstractExtHostExtensionService._applyExtensionsDelta(this._globalRegistry, this._myRegistry, extensionsDelta); + this._globalRegistry.set(globalRegistry.getAllExtensionDescriptions()); + this._myRegistry.set(myExtensions); return this._startExtensionHost(); } @@ -875,32 +892,13 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme public async $deltaExtensions(extensionsDelta: IExtensionDescriptionDelta): Promise { extensionsDelta.toAdd.forEach((extension) => (extension).extensionLocation = URI.revive(extension.extensionLocation)); - this._globalRegistry.deltaExtensions(extensionsDelta.toAdd, extensionsDelta.toRemove); - - const myExtensions = extensionIdentifiersArrayToSet(this._myRegistry.getAllExtensionDescriptions().map(extension => extension.identifier)); - for (const extensionId of extensionsDelta.myToRemove) { - myExtensions.delete(ExtensionIdentifier.toKey(extensionId)); - } - for (const extensionId of extensionsDelta.myToAdd) { - myExtensions.add(ExtensionIdentifier.toKey(extensionId)); - } - this._myRegistry.set(filterExtensions(this._globalRegistry, myExtensions)); - - console.log(`TODO: update the extension path trie!`); - - // const trie = await this.getExtensionPathIndex(); - - // await Promise.all(toRemove.map(async (extensionId) => { - // const extensionDescription = this._myRegistry.getExtensionDescription(extensionId); - // if (extensionDescription) { - // trie.delete(await this._realPathExtensionUri(extensionDescription.extensionLocation)); - // } - // })); - - // await Promise.all(toAdd.map(async (extensionDescription) => { - // const realpathUri = await this._realPathExtensionUri(extensionDescription.extensionLocation); - // trie.set(realpathUri, extensionDescription); - // })); + // First build up and update the trie and only afterwards apply the delta + const { globalRegistry, myExtensions } = AbstractExtHostExtensionService._applyExtensionsDelta(this._globalRegistry, this._myRegistry, extensionsDelta); + const newSearchTree = await this._createExtensionPathIndex(myExtensions); + const extensionsPaths = await this.getExtensionPathIndex(); + extensionsPaths.setSearchTree(newSearchTree); + this._globalRegistry.set(globalRegistry.getAllExtensionDescriptions()); + this._myRegistry.set(myExtensions); return Promise.resolve(undefined); } @@ -972,7 +970,7 @@ export interface IExtHostExtensionService extends AbstractExtHostExtensionServic activateByIdWithErrors(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise; getExtensionExports(extensionId: ExtensionIdentifier): IExtensionAPI | null | undefined; getExtensionRegistry(): Promise; - getExtensionPathIndex(): Promise>; + getExtensionPathIndex(): Promise; registerRemoteAuthorityResolver(authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver): vscode.Disposable; onDidChangeRemoteConnectionData: Event; @@ -1031,3 +1029,22 @@ function getRemoteAuthorityPrefix(remoteAuthority: string): string { } return remoteAuthority.substring(0, plusIndex); } + +export class ExtensionPaths { + + constructor( + private _searchTree: TernarySearchTree + ) { } + + setSearchTree(searchTree: TernarySearchTree): void { + this._searchTree = searchTree; + } + + findSubstr(key: URI): IExtensionDescription | undefined { + return this._searchTree.findSubstr(key); + } + + forEach(callback: (value: IExtensionDescription, index: URI) => any): void { + return this._searchTree.forEach(callback); + } +} diff --git a/src/vs/workbench/api/common/extHostRequireInterceptor.ts b/src/vs/workbench/api/common/extHostRequireInterceptor.ts index 9ce9a8f402f..459f32df428 100644 --- a/src/vs/workbench/api/common/extHostRequireInterceptor.ts +++ b/src/vs/workbench/api/common/extHostRequireInterceptor.ts @@ -4,19 +4,18 @@ *--------------------------------------------------------------------------------------------*/ import * as performance from 'vs/base/common/performance'; -import { TernarySearchTree } from 'vs/base/common/map'; import { URI } from 'vs/base/common/uri'; import { MainThreadTelemetryShape, MainContext } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostConfigProvider, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import * as vscode from 'vscode'; -import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IExtensionApiFactory } from 'vs/workbench/api/common/extHost.api.impl'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; +import { ExtensionPaths, IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; import { platform } from 'vs/base/common/process'; import { ILogService } from 'vs/platform/log/common/log'; import { escapeRegExpCharacters } from 'vs/base/common/strings'; @@ -156,7 +155,7 @@ class VSCodeNodeModuleFactory implements INodeModuleFactory { constructor( private readonly _apiFactory: IExtensionApiFactory, - private readonly _extensionPaths: TernarySearchTree, + private readonly _extensionPaths: ExtensionPaths, private readonly _extensionRegistry: ExtensionDescriptionRegistry, private readonly _configProvider: ExtHostConfigProvider, private readonly _logService: ILogService, @@ -208,7 +207,7 @@ class KeytarNodeModuleFactory implements INodeModuleFactory { private _impl: IKeytarModule; constructor( - private readonly _extensionPaths: TernarySearchTree, + private readonly _extensionPaths: ExtensionPaths, @IExtHostRpcService rpcService: IExtHostRpcService, @IExtHostInitDataService initData: IExtHostInitDataService, @@ -303,7 +302,7 @@ class OpenNodeModuleFactory implements INodeModuleFactory { private _mainThreadTelemetry: MainThreadTelemetryShape; constructor( - private readonly _extensionPaths: TernarySearchTree, + private readonly _extensionPaths: ExtensionPaths, private readonly _appUriScheme: string, @IExtHostRpcService rpcService: IExtHostRpcService, ) { From 0b2c629d212c622e17262671d82612bf5ed0bd92 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 20 Apr 2022 17:01:58 +0300 Subject: [PATCH 175/245] Remove left-over console log calls --- src/vs/workbench/api/common/extHostExtensionService.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index f67924248f5..101f6f4a95b 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -714,11 +714,6 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme } private _startExtensionHost(): Promise { - - console.log(`$startExtensionHost CALLED!`); - console.log(`myExtensions: ${this._myRegistry.getAllExtensionDescriptions().length} extensions`); - console.log(`allExtensions: ${this._globalRegistry.getAllExtensionDescriptions().length} extensions`); - if (this._started) { throw new Error(`Extension host is already started!`); } From 8627bd25691a0daf5f03391ec79a242733252c7a Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Apr 2022 16:19:22 +0200 Subject: [PATCH 176/245] add policy admx/adml files --- build/filters.js | 2 +- resources/policies/codeoss.admx | 48 +++++++++++++++++++++++++++ resources/policies/en-US/codeoss.adml | 23 +++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 resources/policies/codeoss.admx create mode 100644 resources/policies/en-US/codeoss.adml diff --git a/build/filters.js b/build/filters.js index f462566d0f4..c2a0a52bdb9 100644 --- a/build/filters.js +++ b/build/filters.js @@ -112,7 +112,7 @@ module.exports.indentationFilter = [ '!src/vs/*/**/*.d.ts', '!src/typings/**/*.d.ts', '!extensions/**/*.d.ts', - '!**/*.{svg,exe,png,bmp,jpg,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe,ico,icns,plist,opus}', + '!**/*.{svg,exe,png,bmp,jpg,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe,ico,icns,plist,opus,admx,adml}', '!build/{lib,download,linux,darwin}/**/*.js', '!build/**/*.sh', '!build/azure-pipelines/**/*.js', diff --git a/resources/policies/codeoss.admx b/resources/policies/codeoss.admx new file mode 100644 index 00000000000..916f503b782 --- /dev/null +++ b/resources/policies/codeoss.admx @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + none + + + + + manual + + + + + start + + + + + default + + + + + + + diff --git a/resources/policies/en-US/codeoss.adml b/resources/policies/en-US/codeoss.adml new file mode 100644 index 00000000000..f79b1778ec4 --- /dev/null +++ b/resources/policies/en-US/codeoss.adml @@ -0,0 +1,23 @@ + + + + + + + Code - OSS 1.67 or later + Code - OSS + Update + Update Mode + Configure whether you receive automatic updates. Requires a restart after change. The updates are fetched from a Microsoft online service. + Disable updates. + Disable automatic background update checks. Updates will be available if you manually check for updates. + Check for updates only on startup. Disable automatic background update checks. + Enable automatic update checks. Code will check for updates automatically and periodically. + + + + + + + + From 154ce41f432ee4f98882767c1afad32745a78ac1 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Apr 2022 16:23:33 +0200 Subject: [PATCH 177/245] rename --- resources/policies/{codeoss.admx => Code.admx} | 0 resources/policies/en-US/{codeoss.adml => Code.adml} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename resources/policies/{codeoss.admx => Code.admx} (100%) rename resources/policies/en-US/{codeoss.adml => Code.adml} (100%) diff --git a/resources/policies/codeoss.admx b/resources/policies/Code.admx similarity index 100% rename from resources/policies/codeoss.admx rename to resources/policies/Code.admx diff --git a/resources/policies/en-US/codeoss.adml b/resources/policies/en-US/Code.adml similarity index 100% rename from resources/policies/en-US/codeoss.adml rename to resources/policies/en-US/Code.adml From b16e71ad2a7b90974d24799a40878cf1c9f80402 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 20 Apr 2022 16:28:31 +0200 Subject: [PATCH 178/245] Fixes #147724. --- .../bracketPairsTree/brackets.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/brackets.ts b/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/brackets.ts index 9dc3e7e0b40..280cb3b8deb 100644 --- a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/brackets.ts +++ b/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/brackets.ts @@ -114,12 +114,8 @@ export class LanguageAgnosticBracketTokens { } public didLanguageChange(languageId: string): boolean { - const existing = this.languageIdToBracketTokens.get(languageId); - if (!existing) { - return false; - } - const newRegExpStr = BracketTokens.createFromLanguage(this.getLanguageConfiguration(languageId), this.denseKeyProvider).getRegExpStr(); - return existing.getRegExpStr() !== newRegExpStr; + // Report a change whenever the language configuration updates. + return this.languageIdToBracketTokens.has(languageId); } getSingleLanguageBracketTokens(languageId: string): BracketTokens { From 8c8e9fe6a3dfa357e677ef624d24ce1792e3583d Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Apr 2022 16:30:12 +0200 Subject: [PATCH 179/245] bump distro --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 2fc5a5bea62..e805069a55f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.67.0", - "distro": "6cb70cc8b7fd45faf408753b1ff26f59e62112aa", + "distro": "f51315347a8ff42e920fd7db3e6593051591f74d", "author": { "name": "Microsoft Corporation" }, @@ -230,4 +230,4 @@ "elliptic": "^6.5.3", "nwmatcher": "^1.4.4" } -} +} \ No newline at end of file From 882fdd77b20a1c37d1e5f6f388971d1fa9ed8c0b Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Apr 2022 16:34:41 +0200 Subject: [PATCH 180/245] include policies in win32 --- build/gulpfile.vscode.js | 2 ++ resources/{ => win32}/policies/Code.admx | 0 resources/{ => win32}/policies/en-US/Code.adml | 0 3 files changed, 2 insertions(+) rename resources/{ => win32}/policies/Code.admx (100%) rename resources/{ => win32}/policies/en-US/Code.adml (100%) diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 2a67103d2b9..e6c30fec111 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -330,6 +330,8 @@ 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('resources/win32/policies/**', { base: 'resources/win32' })); } else if (platform === 'linux') { result = es.merge(result, gulp.src('resources/linux/bin/code.sh', { base: '.' }) .pipe(replace('@@PRODNAME@@', product.nameLong)) diff --git a/resources/policies/Code.admx b/resources/win32/policies/Code.admx similarity index 100% rename from resources/policies/Code.admx rename to resources/win32/policies/Code.admx diff --git a/resources/policies/en-US/Code.adml b/resources/win32/policies/en-US/Code.adml similarity index 100% rename from resources/policies/en-US/Code.adml rename to resources/win32/policies/en-US/Code.adml From 5ca4d13b40a6b72a39b7ebf55941cdfa2d0558f6 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 20 Apr 2022 16:36:02 +0200 Subject: [PATCH 181/245] bump distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e805069a55f..d612b999635 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.67.0", - "distro": "f51315347a8ff42e920fd7db3e6593051591f74d", + "distro": "12a90eb4bfd1f388f5a039a95223b178d14afd6d", "author": { "name": "Microsoft Corporation" }, From 21cc113c82ebf328b19e7f2e8301347ebe894b9d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 20 Apr 2022 17:36:10 +0200 Subject: [PATCH 182/245] Smoke test fail on all platforms (fix #147749) --- .../services/dialogs/common/dialogService.ts | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/vs/workbench/services/dialogs/common/dialogService.ts b/src/vs/workbench/services/dialogs/common/dialogService.ts index d17f9fbbf5c..a4bda254186 100644 --- a/src/vs/workbench/services/dialogs/common/dialogService.ts +++ b/src/vs/workbench/services/dialogs/common/dialogService.ts @@ -8,6 +8,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IConfirmation, IConfirmationResult, IDialogOptions, IDialogService, IInput, IInputResult, IShowResult } from 'vs/platform/dialogs/common/dialogs'; import { DialogsModel } from 'vs/workbench/common/dialogs'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; export class DialogService extends Disposable implements IDialogService { @@ -19,25 +20,53 @@ export class DialogService extends Disposable implements IDialogService { readonly onDidShowDialog = this.model.onDidShowDialog; + constructor(@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService) { + super(); + } + + private skipDialogs(): boolean { + if (this.environmentService.isExtensionDevelopment && this.environmentService.extensionTestsLocationURI) { + return true; // integration tests + } + + return !!this.environmentService.enableSmokeTestDriver; // smoke tests + } + async confirm(confirmation: IConfirmation): Promise { + if (this.skipDialogs()) { + return { confirmed: true }; + } + const handle = this.model.show({ confirmArgs: { confirmation } }); return await handle.result as IConfirmationResult; } async show(severity: Severity, message: string, buttons?: string[], options?: IDialogOptions): Promise { + if (this.skipDialogs()) { + throw new Error('Dialogs are disabled in tests'); + } + const handle = this.model.show({ showArgs: { severity, message, buttons, options } }); return await handle.result as IShowResult; } async input(severity: Severity, message: string, buttons: string[], inputs: IInput[], options?: IDialogOptions): Promise { + if (this.skipDialogs()) { + throw new Error('Dialogs are disabled in tests'); + } + const handle = this.model.show({ inputArgs: { severity, message, buttons, inputs, options } }); return await handle.result as IInputResult; } async about(): Promise { + if (this.skipDialogs()) { + throw new Error('Dialogs are disabled in tests'); + } + const handle = this.model.show({}); await handle.result; } From 7e887b76ea4d0fbee838b4240352ce57dfd1895d Mon Sep 17 00:00:00 2001 From: Andrii Dieiev Date: Wed, 20 Apr 2022 19:40:18 +0300 Subject: [PATCH 183/245] Fix version in error message (#147750) --- .../src/languageFeatures/sourceDefinition.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/sourceDefinition.ts b/extensions/typescript-language-features/src/languageFeatures/sourceDefinition.ts index 8107d92b109..d020d265995 100644 --- a/extensions/typescript-language-features/src/languageFeatures/sourceDefinition.ts +++ b/extensions/typescript-language-features/src/languageFeatures/sourceDefinition.ts @@ -55,7 +55,7 @@ class SourceDefinitionCommand implements Command { public async execute() { if (this.client.apiVersion.lt(SourceDefinitionCommand.minVersion)) { - vscode.window.showErrorMessage(localize('error.unsupportedVersion', "Go to Source Definition failed. Requires TypeScript 4.2+.")); + vscode.window.showErrorMessage(localize('error.unsupportedVersion', "Go to Source Definition failed. Requires TypeScript 4.7+.")); return; } From 87191b089d1a258b114f612db094e8048cefdf3f Mon Sep 17 00:00:00 2001 From: deepak1556 Date: Thu, 21 Apr 2022 01:59:16 +0900 Subject: [PATCH 184/245] ci: fix downloading linux server dependencies --- build/azure-pipelines/linux/product-build-linux-client.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/azure-pipelines/linux/product-build-linux-client.yml b/build/azure-pipelines/linux/product-build-linux-client.yml index d5be0e3b606..bbb5b8dc597 100644 --- a/build/azure-pipelines/linux/product-build-linux-client.yml +++ b/build/azure-pipelines/linux/product-build-linux-client.yml @@ -21,7 +21,7 @@ steps: artifact: reh_node_modules-$(VSCODE_ARCH) path: $(Build.ArtifactStagingDirectory) displayName: Download server build dependencies - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) + condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'armhf')) - script: | set -e From e0a3f8d33a46ca752d043be329a1543a98003613 Mon Sep 17 00:00:00 2001 From: aamunger Date: Wed, 20 Apr 2022 10:57:38 -0700 Subject: [PATCH 185/245] use css selector that includes IW scrollbar --- src/vs/workbench/contrib/notebook/browser/media/notebook.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index a660892ff1f..e4220b57b14 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -429,7 +429,7 @@ outline: none !important; } -.monaco-workbench .notebookOverlay.notebook-editor-editable > .cell-list-container > .monaco-list > .monaco-scrollable-element > .scrollbar.visible { +.monaco-workbench .notebookOverlay.notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .scrollbar.visible { z-index: var(--z-index-notebook-scrollbar); cursor: default; } From d7dbb0b8a2071de5b76ae6e18961bc6ef475aea1 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 20 Apr 2022 11:08:51 -0700 Subject: [PATCH 186/245] Also escape pipe character in bash shell. #145265 --- src/vs/workbench/contrib/debug/node/terminals.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/debug/node/terminals.ts b/src/vs/workbench/contrib/debug/node/terminals.ts index b6c3ced13c5..d4338a5c04c 100644 --- a/src/vs/workbench/contrib/debug/node/terminals.ts +++ b/src/vs/workbench/contrib/debug/node/terminals.ts @@ -121,7 +121,7 @@ export function prepareCommand(shell: string, args: string[], cwd?: string, env? quote = (s: string) => { s = s.replace(/\"/g, '""'); - s = s.replace(/([> s.includes(char)) || s.length === 0) ? `"${s}"` : s; }; @@ -139,7 +139,7 @@ export function prepareCommand(shell: string, args: string[], cwd?: string, env? if (value === null) { command += `set "${key}=" && `; } else { - value = value.replace(/[\^\&\|\<\>]/g, s => `^${s}`); + value = value.replace(/[^&|<>]/g, s => `^${s}`); command += `set "${key}=${value}" && `; } } @@ -155,7 +155,7 @@ export function prepareCommand(shell: string, args: string[], cwd?: string, env? case ShellType.bash: { quote = (s: string) => { - s = s.replace(/(["'\\\$!><#()\[\]*&^])/g, '\\$1'); + s = s.replace(/(["'\\\$!><#()\[\]*&^|])/g, '\\$1'); return (' ;'.split('').some(char => s.includes(char)) || s.length === 0) ? `"${s}"` : s; }; From f171e5ecdef64be7ebd1d3b51957ff108b3d74ec Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Wed, 20 Apr 2022 12:03:16 -0700 Subject: [PATCH 187/245] Improve language filter placement, fixes #145711 (#147727) --- .../contrib/preferences/browser/settingsEditor2.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 6ac2b721752..3afedd34c39 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -838,7 +838,11 @@ export class SettingsEditor2 extends EditorPane { } })); this._register(this.settingRenderers.onApplyLanguageFilter((lang: string) => { - this.focusSearch(`@${LANGUAGE_SETTING_TAG}${lang}`); + if (this.searchWidget) { + // Prepend the language filter to the query. + const newQuery = `@${LANGUAGE_SETTING_TAG}${lang} ${this.searchWidget.getValue().trimStart()}`; + this.focusSearch(newQuery, false); + } })); this.settingsTree = this._register(this.instantiationService.createInstance(SettingsTree, From 042921eee9e3c6b214271ca8b6153a5963c21a92 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Wed, 20 Apr 2022 12:05:19 -0700 Subject: [PATCH 188/245] migrate non-encrypted strings in keyring (#147785) --- .../api/browser/mainThreadSecretState.ts | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadSecretState.ts b/src/vs/workbench/api/browser/mainThreadSecretState.ts index 1a131d41518..539315d7c08 100644 --- a/src/vs/workbench/api/browser/mainThreadSecretState.ts +++ b/src/vs/workbench/api/browser/mainThreadSecretState.ts @@ -8,6 +8,7 @@ import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/ext import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; import { IEncryptionService } from 'vs/workbench/services/encryption/common/encryptionService'; import { ExtHostContext, ExtHostSecretStateShape, MainContext, MainThreadSecretStateShape } from '../common/extHost.protocol'; +import { ILogService } from 'vs/platform/log/common/log'; @extHostNamedCustomer(MainContext.MainThreadSecretState) export class MainThreadSecretState extends Disposable implements MainThreadSecretStateShape { @@ -19,6 +20,7 @@ export class MainThreadSecretState extends Disposable implements MainThreadSecre extHostContext: IExtHostContext, @ICredentialsService private readonly credentialsService: ICredentialsService, @IEncryptionService private readonly encryptionService: IEncryptionService, + @ILogService private readonly logService: ILogService, ) { super(); this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostSecretState); @@ -36,7 +38,26 @@ export class MainThreadSecretState extends Disposable implements MainThreadSecre async $getPassword(extensionId: string, key: string): Promise { const fullKey = await this.getFullKey(extensionId); const password = await this.credentialsService.getPassword(fullKey, key); - const decrypted = password && await this.encryptionService.decrypt(password); + if (!password) { + return undefined; + } + + let decrypted: string | null; + try { + decrypted = await this.encryptionService.decrypt(password); + } catch (e) { + this.logService.error(e); + + // If we are on a platform that newly started encrypting secrets before storing them, + // then passwords previously stored were stored un-encrypted (NOTE: but still being stored in a secure keyring). + // When we try to decrypt a password that wasn't encrypted previously, the encryption service will throw. + // To recover gracefully, we first try to encrypt & store the password (essentially migrating the secret to the new format) + // and then we try to read it and decrypt again. + const encryptedForSet = await this.encryptionService.encrypt(password); + await this.credentialsService.setPassword(fullKey, key, encryptedForSet); + const passwordEncrypted = await this.credentialsService.getPassword(fullKey, key); + decrypted = passwordEncrypted && await this.encryptionService.decrypt(passwordEncrypted); + } if (decrypted) { try { @@ -59,7 +80,7 @@ export class MainThreadSecretState extends Disposable implements MainThreadSecre content: value }); const encrypted = await this.encryptionService.encrypt(toEncrypt); - return this.credentialsService.setPassword(fullKey, key, encrypted); + return await this.credentialsService.setPassword(fullKey, key, encrypted); } async $deletePassword(extensionId: string, key: string): Promise { From f291d53d74bac9a9e1d2bf1f76e3ce502caa9faa Mon Sep 17 00:00:00 2001 From: Raymond Zhao Date: Wed, 20 Apr 2022 12:58:13 -0700 Subject: [PATCH 189/245] Add online services option to the search funnel (#147643) --- .../browser/preferences.contribution.ts | 46 ++-------------- .../preferences/browser/settingsSearchMenu.ts | 55 ++++++++++++------- 2 files changed, 38 insertions(+), 63 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index 42365a36d4f..b5230a5af22 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -34,7 +34,7 @@ import { ConfigureLanguageBasedSettingsAction } from 'vs/workbench/contrib/prefe import { SettingsEditorContribution } from 'vs/workbench/contrib/preferences/browser/preferencesEditor'; import { preferencesOpenSettingsIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons'; import { SettingsEditor2, SettingsFocusContext } from 'vs/workbench/contrib/preferences/browser/settingsEditor2'; -import { CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, CONTEXT_KEYBINDING_FOCUS, CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, CONTEXT_WHEN_FOCUS, KEYBINDINGS_EDITOR_COMMAND_ADD, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND_TITLE, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SEARCH, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_EXTENSION_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS, MODIFIED_SETTING_TAG, REQUIRE_TRUSTED_WORKSPACE_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences'; +import { CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, CONTEXT_KEYBINDING_FOCUS, CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR, CONTEXT_SETTINGS_ROW_FOCUS, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, CONTEXT_WHEN_FOCUS, KEYBINDINGS_EDITOR_COMMAND_ADD, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND_TITLE, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_DEFINE_WHEN, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SEARCH, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_EXTENSION_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS, REQUIRE_TRUSTED_WORKSPACE_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/contrib/preferences/common/preferences'; import { PreferencesContribution } from 'vs/workbench/contrib/preferences/common/preferencesContribution'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -54,7 +54,6 @@ const SETTINGS_EDITOR_COMMAND_FOCUS_CONTROL = 'settings.action.focusSettingContr const SETTINGS_EDITOR_COMMAND_FOCUS_UP = 'settings.action.focusLevelUp'; const SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON = 'settings.switchToJSON'; -const SETTINGS_EDITOR_COMMAND_FILTER_MODIFIED = 'settings.filterByModified'; const SETTINGS_EDITOR_COMMAND_FILTER_ONLINE = 'settings.filterByOnline'; const SETTINGS_EDITOR_COMMAND_FILTER_TELEMETRY = 'settings.filterByTelemetry'; const SETTINGS_EDITOR_COMMAND_FILTER_UNTRUSTED = 'settings.filterUntrusted'; @@ -393,36 +392,15 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon return accessor.get(IPreferencesService).openFolderSettings({ folderUri: resource }); } }); - registerAction2(class extends Action2 { - constructor() { - super({ - id: SETTINGS_EDITOR_COMMAND_FILTER_MODIFIED, - title: { value: nls.localize('filterModifiedLabel', "Show modified settings"), original: 'Show modified settings' }, - menu: { - id: MenuId.EditorTitle, - group: '1_filter', - order: 1, - when: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR.toNegated()) - } - }); - } - run(accessor: ServicesAccessor, resource: URI) { - const editorPane = accessor.get(IEditorService).activeEditorPane; - if (editorPane instanceof SettingsEditor2) { - editorPane.focusSearch(`@${MODIFIED_SETTING_TAG}`); - } - } - }); registerAction2(class extends Action2 { constructor() { super({ id: SETTINGS_EDITOR_COMMAND_FILTER_ONLINE, - title: { value: nls.localize('filterOnlineServicesLabel', "Show settings for online services"), original: 'Show settings for online services' }, + title: nls.localize({ key: 'miOpenOnlineSettings', comment: ['&& denotes a mnemonic'] }, "&&Online Services Settings"), menu: { - id: MenuId.EditorTitle, - group: '1_filter', + id: MenuId.MenubarPreferencesMenu, + group: '1_settings', order: 2, - when: ContextKeyExpr.and(CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR.toNegated()) } }); } @@ -435,22 +413,6 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon } } }); - MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { - group: '1_settings', - command: { - id: SETTINGS_EDITOR_COMMAND_FILTER_ONLINE, - title: nls.localize({ key: 'miOpenOnlineSettings', comment: ['&& denotes a mnemonic'] }, "&&Online Services Settings") - }, - order: 2 - }); - MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { - group: '2_configuration', - command: { - id: SETTINGS_EDITOR_COMMAND_FILTER_ONLINE, - title: nls.localize('onlineServices', "Online Services Settings") - }, - order: 2 - }); registerAction2(class extends Action2 { constructor() { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts b/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts index 83675b77e00..333d179f818 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsSearchMenu.ts @@ -10,7 +10,7 @@ import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestCont import { localize } from 'vs/nls'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { SuggestEnabledInput } from 'vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput'; -import { EXTENSION_SETTING_TAG, FEATURE_SETTING_TAG, GENERAL_TAG_SETTING_TAG, ID_SETTING_TAG, LANGUAGE_SETTING_TAG, MODIFIED_SETTING_TAG } from 'vs/workbench/contrib/preferences/common/preferences'; +import { EXTENSION_SETTING_TAG, FEATURE_SETTING_TAG, GENERAL_TAG_SETTING_TAG, LANGUAGE_SETTING_TAG, MODIFIED_SETTING_TAG } from 'vs/workbench/contrib/preferences/common/preferences'; export class SettingsSearchFilterDropdownMenuActionViewItem extends DropdownMenuActionViewItem { private readonly suggestController: SuggestController | null; @@ -47,6 +47,9 @@ export class SettingsSearchFilterDropdownMenuActionViewItem extends DropdownMenu } } + /** + * The created action appends a query to the search widget search string. It optionally triggers suggestions. + */ private createAction(id: string, label: string, tooltip: string, queryToAppend: string, triggerSuggest: boolean): IAction { return { id, @@ -60,24 +63,30 @@ export class SettingsSearchFilterDropdownMenuActionViewItem extends DropdownMenu }; } - private createModifiedAction(): IAction { - // The modified action works slightly differently than the other actions. - // It is more like a checkbox on/off toggle. - const queryContainsModifiedTag = this.searchWidget.getValue().split(' ').some(word => word === `@${MODIFIED_SETTING_TAG}`); + /** + * The created action appends a query to the search widget search string, if the query does not exist. + * Otherwise, it removes the query from the search widget search string. + * The action does not trigger suggestions after adding or removing the query. + */ + private createToggleAction(id: string, label: string, tooltip: string, queryToAppend: string): IAction { + const splitCurrentQuery = this.searchWidget.getValue().split(' '); + const queryContainsQueryToAppend = splitCurrentQuery.includes(queryToAppend); return { - id: 'modifiedSettingsSearch', - label: localize('modifiedSettingsSearch', "Modified"), - tooltip: localize('modifiedSettingsSearchTooltip', "View modified settings only"), + id, + label, + tooltip, class: undefined, enabled: true, - checked: queryContainsModifiedTag, + checked: queryContainsQueryToAppend, run: () => { - // Append the tag, otherwise remove it from the query. - if (!queryContainsModifiedTag) { - this.searchWidget.setValue(this.searchWidget.getValue().trimEnd() + ` @${MODIFIED_SETTING_TAG}`); + if (!queryContainsQueryToAppend) { + const trimmedCurrentQuery = this.searchWidget.getValue().trimEnd(); + const newQuery = trimmedCurrentQuery ? trimmedCurrentQuery + ' ' + queryToAppend : queryToAppend; + this.searchWidget.setValue(newQuery); } else { - const queryWithoutModifiedTag = this.searchWidget.getValue().split(' ').filter(word => word !== `@${MODIFIED_SETTING_TAG}`).join(' '); - this.searchWidget.setValue(queryWithoutModifiedTag); + const queryWithRemovedTags = this.searchWidget.getValue().split(' ') + .filter(word => word !== queryToAppend).join(' '); + this.searchWidget.setValue(queryWithRemovedTags); } this.searchWidget.focus(); }, @@ -87,7 +96,12 @@ export class SettingsSearchFilterDropdownMenuActionViewItem extends DropdownMenu getActions(): IAction[] { return [ - this.createModifiedAction(), + this.createToggleAction( + 'modifiedSettingsSearch', + localize('modifiedSettingsSearch', "Modified"), + localize('modifiedSettingsSearchTooltip', "Add or remove modified settings filter"), + `@${MODIFIED_SETTING_TAG}` + ), this.createAction( 'extSettingsSearch', localize('extSettingsSearch', "Extension ID..."), @@ -116,12 +130,11 @@ export class SettingsSearchFilterDropdownMenuActionViewItem extends DropdownMenu `@${LANGUAGE_SETTING_TAG}`, true ), - this.createAction( - 'idSettingsSearch', - localize('idSettingsSearch', "Setting ID..."), - localize('idSettingsSearchTooltip', "Add setting ID filter"), - `@${ID_SETTING_TAG}`, - false + this.createToggleAction( + 'onlineSettingsSearch', + localize('onlineSettingsSearch', "Online services"), + localize('onlineSettingsSearchTooltip', "Show settings for online services"), + '@tag:usesOnlineServices' ) ]; } From 7e2fccd89ae4308b8776658d19b5d43804d9ae3c Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Wed, 20 Apr 2022 15:32:36 -0700 Subject: [PATCH 190/245] fix #147780 (#147798) --- .../vscode-api-tests/src/singlefolder-tests/terminal.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts index 4af3c975e70..33a5fe95eb2 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts @@ -26,6 +26,7 @@ import { assertNoRpc, poll } from '../utils'; await config.update('gpuAcceleration', 'off', ConfigurationTarget.Global); // Disable env var relaunch for tests to prevent terminals relaunching themselves await config.update('environmentChangesRelaunch', false, ConfigurationTarget.Global); + await config.update('shellIntegration.enabled', false); }); suite('Terminal', () => { From 3124b9a5cc2f07860765980b459bd0900f9a8229 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 20 Apr 2022 17:29:54 -0700 Subject: [PATCH 191/245] fixes #147644 --- .../workbench/contrib/files/browser/fileActions.contribution.ts | 2 +- src/vs/workbench/contrib/files/browser/fileActions.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index 3825aad9830..71422933855 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -206,7 +206,7 @@ appendToCommandPalette(SAVE_FILES_COMMAND_ID, { value: nls.localize('saveFiles', appendToCommandPalette(REVERT_FILE_COMMAND_ID, { value: nls.localize('revert', "Revert File"), original: 'Revert File' }, category); appendToCommandPalette(COMPARE_WITH_SAVED_COMMAND_ID, { value: nls.localize('compareActiveWithSaved', "Compare Active File with Saved"), original: 'Compare Active File with Saved' }, category); appendToCommandPalette(SAVE_FILE_AS_COMMAND_ID, { value: SAVE_FILE_AS_LABEL, original: 'Save As...' }, category); -appendToCommandPalette(NEW_FILE_COMMAND_ID, { value: NEW_FILE_LABEL, original: 'New Text File' }, category, WorkspaceFolderCountContext.notEqualsTo('0')); +appendToCommandPalette(NEW_FILE_COMMAND_ID, { value: NEW_FILE_LABEL, original: 'New File' }, category, WorkspaceFolderCountContext.notEqualsTo('0')); appendToCommandPalette(NEW_FOLDER_COMMAND_ID, { value: NEW_FOLDER_LABEL, original: 'New Folder' }, category, WorkspaceFolderCountContext.notEqualsTo('0')); appendToCommandPalette(NEW_UNTITLED_FILE_COMMAND_ID, { value: NEW_UNTITLED_FILE_LABEL, original: 'New Untitled File' }, category); diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index e482338009e..d3100d24946 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -53,7 +53,7 @@ import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteA import { IPathService } from 'vs/workbench/services/path/common/pathService'; export const NEW_FILE_COMMAND_ID = 'explorer.newFile'; -export const NEW_FILE_LABEL = nls.localize('newFile', "New Text File"); +export const NEW_FILE_LABEL = nls.localize('newFile', "New File"); export const NEW_FOLDER_COMMAND_ID = 'explorer.newFolder'; export const NEW_FOLDER_LABEL = nls.localize('newFolder', "New Folder"); export const TRIGGER_RENAME_LABEL = nls.localize('rename', "Rename"); From 8cad47446f29c889b42f1678723fbc493d08d82f Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 20 Apr 2022 17:43:56 -0700 Subject: [PATCH 192/245] updates to the the new file flow refs #145046 --- .../browser/untitledTextEditorHint.ts | 34 +++++++++---------- .../files/browser/views/explorerView.ts | 2 +- .../common/gettingStartedContent.ts | 4 +-- .../common/newFile.contribution.ts | 2 +- .../actions/common/menusExtensionPoint.ts | 2 +- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint.ts b/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint.ts index c5e1a0cba17..353965fd9fa 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint.ts @@ -106,6 +106,20 @@ class UntitledTextEditorHintContentWidget implements IContentWidget { if (!this.domNode) { this.domNode = $('.untitled-hint'); this.domNode.style.width = 'max-content'; + + const editorType = $('a.editor-type'); + editorType.style.cursor = 'pointer'; + editorType.innerText = localize('notLookingForTextEditor', "Not looking for a text editor?"); + const selectEditorTypeKeyBinding = this.keybindingService.lookupKeybinding('welcome.showNewFileEntries'); + const selectEditorTypeKeybindingLabel = selectEditorTypeKeyBinding?.getLabel(); + if (selectEditorTypeKeybindingLabel) { + editorType.title = localize('keyboardBindingTooltip', "{0}", selectEditorTypeKeybindingLabel); + } + this.domNode.appendChild(editorType); + + this.domNode.appendChild($('br')); + this.domNode.appendChild($('br')); + const language = $('a.language-mode'); language.style.cursor = 'pointer'; language.innerText = localize('selectAlanguage2', "Select a language"); @@ -116,20 +130,6 @@ class UntitledTextEditorHintContentWidget implements IContentWidget { } this.domNode.appendChild(language); - const or = $('span'); - or.innerText = localize('or', " or ",); - this.domNode.appendChild(or); - - const chooseEditor = $('a.choose-editor'); - chooseEditor.style.cursor = 'pointer'; - chooseEditor.innerText = localize('chooseEditor', "select a different editor"); - const chooseEditorKeyBinding = this.keybindingService.lookupKeybinding('welcome.showNewFileEntries'); - const chooseEditorKeybindingLabel = chooseEditorKeyBinding?.getLabel(); - if (chooseEditorKeybindingLabel) { - chooseEditor.title = localize('chooseEditorBindingTooltip', "{0}", chooseEditorKeybindingLabel); - } - this.domNode.appendChild(chooseEditor); - const toGetStarted = $('span'); toGetStarted.innerText = localize('toGetStarted', " to get started. Start typing to dismiss, or ",); this.domNode.appendChild(toGetStarted); @@ -165,9 +165,9 @@ class UntitledTextEditorHintContentWidget implements IContentWidget { this.editorGroupsService.activeGroup.closeEditor(activeEditorInput, { preserveFocus: true }); } }; - this.toDispose.push(dom.addDisposableListener(chooseEditor, 'click', chooseEditorOnClickOrTap)); - this.toDispose.push(dom.addDisposableListener(chooseEditor, GestureEventType.Tap, chooseEditorOnClickOrTap)); - this.toDispose.push(Gesture.addTarget(chooseEditor)); + this.toDispose.push(dom.addDisposableListener(editorType, 'click', chooseEditorOnClickOrTap)); + this.toDispose.push(dom.addDisposableListener(editorType, GestureEventType.Tap, chooseEditorOnClickOrTap)); + this.toDispose.push(Gesture.addTarget(editorType)); const dontShowOnClickOrTap = () => { this.configurationService.updateValue(untitledTextEditorHintSetting, 'hidden'); diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index a7885058f6a..c82cc35150a 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -891,7 +891,7 @@ registerAction2(class extends Action2 { constructor() { super({ id: 'workbench.files.action.createFileFromExplorer', - title: nls.localize('createNewEditor', "New Editor"), + title: nls.localize('createNewFile', "New File"), f1: false, icon: Codicon.newFile, precondition: ExplorerResourceNotReadonlyContext, diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts index ac4305b3e3d..a091be87b19 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/common/gettingStartedContent.ts @@ -57,8 +57,8 @@ type GettingStartedStartEntryContent = BuiltinGettingStartedStartEntry[]; export const startEntries: GettingStartedStartEntryContent = [ { id: 'welcome.showNewFileEntries', - title: localize('gettingStarted.newEditor.title', "New Editor..."), - description: localize('gettingStarted.newEditor.description', "Open a new untitled file, notebook, or custom editor."), + title: localize('gettingStarted.newFile.title', "New File..."), + description: localize('gettingStarted.newFile.description', "Open a new untitled file, notebook, or custom editor."), icon: Codicon.newFile, content: { type: 'startEntry', diff --git a/src/vs/workbench/contrib/welcomeViews/common/newFile.contribution.ts b/src/vs/workbench/contrib/welcomeViews/common/newFile.contribution.ts index 3c20b55f28a..8b87c52e229 100644 --- a/src/vs/workbench/contrib/welcomeViews/common/newFile.contribution.ts +++ b/src/vs/workbench/contrib/welcomeViews/common/newFile.contribution.ts @@ -25,7 +25,7 @@ registerAction2(class extends Action2 { constructor() { super({ id: 'welcome.showNewFileEntries', - title: { value: localize('welcome.newEditor', "New Editor..."), original: 'New Editor...' }, + title: { value: localize('welcome.newFile', "New File..."), original: 'New File...' }, category, f1: true, keybinding: { diff --git a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts index 3aa9a036090..7e06c120f95 100644 --- a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts +++ b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts @@ -242,7 +242,7 @@ const apiMenus: IAPIMenu[] = [ { key: 'file/newFile', id: MenuId.NewFile, - description: localize('file.newEditor', "The 'New Editor...' quick pick, shown on welcome page and File menu."), + description: localize('file.newFile', "The 'New File...' quick pick, shown on welcome page and File menu."), supportsSubmenus: false, }, { From bd95a8d4516d590256eefecfce465a2f10e8f5e5 Mon Sep 17 00:00:00 2001 From: Jackson Kearl Date: Wed, 20 Apr 2022 20:38:20 -0700 Subject: [PATCH 193/245] Implement language detection for notebook cells (#147537) * Implement language detection for notebook cells * Add lightbulb for normal text editors as well Clean up mapping of language id's in neural model Add config to opt out of language detection hints --- .../browser/parts/editor/editorStatus.ts | 2 +- .../browser/workbench.contribution.ts | 13 ++ .../browser/languageDetection.contribution.ts | 148 ++++++++++++++++++ .../cellStatusBar/statusBarProviders.ts | 74 ++++++++- .../browser/controller/editActions.ts | 66 +++++--- .../notebook/browser/notebookBrowser.ts | 1 + .../browser/languageDetectionSimpleWorker.ts | 51 +++--- .../languageDetectionWorkerServiceImpl.ts | 58 ++++--- .../common/languageDetectionWorkerService.ts | 3 +- src/vs/workbench/workbench.common.main.ts | 3 + 10 files changed, 358 insertions(+), 61 deletions(-) create mode 100644 src/vs/workbench/contrib/languageDetection/browser/languageDetection.contribution.ts diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index 92091e6054d..7571481ae97 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -1202,7 +1202,7 @@ export class ChangeLanguageAction extends Action { if (resource) { // Detect languages since we are in an untitled file let languageId: string | undefined = withNullAsUndefined(this.languageService.guessLanguageIdByFilepathOrFirstLine(resource, textModel.getLineContent(1))); - if (!languageId) { + if (!languageId || languageId === 'unknown') { detectedLanguage = await this.languageDetectionService.detectLanguage(resource); languageId = detectedLanguage; } diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 77d6942b08a..903ca298a14 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -111,6 +111,19 @@ const registry = Registry.as(ConfigurationExtensions.Con tags: ['experimental'], description: localize('workbench.editor.preferBasedLanguageDetection', "When enabled, a language detection model that takes into account editor history will be given higher precedence."), }, + 'workbench.editor.languageDetectionHints': { + type: 'string', + default: 'always', + tags: ['experimental'], + enum: ['always', 'notebookEditors', 'textEditors', 'never'], + description: localize('workbench.editor.showLanguageDetectionHints', "When enabled, shows a status bar quick fix when the editor language doesn't match detected content language."), + enumDescriptions: [ + localize('workbench.editor.showLanguageDetectionHints.always', "Show show language detection quick fixes in both notebooks and untitled editors"), + localize('workbench.editor.showLanguageDetectionHints.notebook', "Only show language detection quick fixes in notebooks"), + localize('workbench.editor.showLanguageDetectionHints.editors', "Only show language detection quick fixes in untitled editors"), + localize('workbench.editor.showLanguageDetectionHints.never', "Never show language quick fixes"), + ] + }, 'workbench.editor.tabCloseButton': { 'type': 'string', 'enum': ['left', 'right', 'off'], diff --git a/src/vs/workbench/contrib/languageDetection/browser/languageDetection.contribution.ts b/src/vs/workbench/contrib/languageDetection/browser/languageDetection.contribution.ts new file mode 100644 index 00000000000..3df19dec7a5 --- /dev/null +++ b/src/vs/workbench/contrib/languageDetection/browser/languageDetection.contribution.ts @@ -0,0 +1,148 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { getCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { localize } from 'vs/nls'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar'; +import { ILanguageDetectionService } from 'vs/workbench/services/languageDetection/common/languageDetectionWorkerService'; +import { ThrottledDelayer } from 'vs/base/common/async'; +import { ILanguageService } from 'vs/editor/common/languages/language'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +import { registerAction2, Action2 } from 'vs/platform/actions/common/actions'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { NOTEBOOK_EDITOR_EDITABLE } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { Schemas } from 'vs/base/common/network'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; + +const detectLanguageCommandId = 'editor.detectLanguage'; + +class LanguageDetectionStatusContribution implements IWorkbenchContribution { + + private static readonly _id = 'status.languageDetectionStatus'; + + private readonly _disposables = new DisposableStore(); + private _combinedEntry?: IStatusbarEntryAccessor; + private _delayer = new ThrottledDelayer(1000); + private _renderDisposables = new DisposableStore(); + + constructor( + @ILanguageDetectionService private readonly _languageDetectionService: ILanguageDetectionService, + @IStatusbarService private readonly _statusBarService: IStatusbarService, + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IEditorService private readonly _editorService: IEditorService, + @ILanguageService private readonly _languageService: ILanguageService, + @IKeybindingService private readonly _keybindingService: IKeybindingService, + ) { + _editorService.onDidActiveEditorChange(() => this._update(true), this, this._disposables); + this._update(false); + } + + dispose(): void { + this._disposables.dispose(); + this._delayer.dispose(); + this._combinedEntry?.dispose(); + this._renderDisposables.dispose(); + } + + private _update(clear: boolean): void { + if (clear) { + this._combinedEntry?.dispose(); + this._combinedEntry = undefined; + } + this._delayer.trigger(() => this._doUpdate()); + } + + private async _doUpdate(): Promise { + const editor = getCodeEditor(this._editorService.activeTextEditorControl); + + this._renderDisposables.clear(); + + // update when editor language changes + editor?.onDidChangeModelLanguage(() => this._update(true), this, this._renderDisposables); + editor?.onDidChangeModelContent(() => this._update(false), this, this._renderDisposables); + const editorModel = editor?.getModel(); + const editorUri = editorModel?.uri; + const existingId = editorModel?.getLanguageId(); + const enablementConfig = this._configurationService.getValue('workbench.editor.languageDetectionHints'); + const enabled = enablementConfig === 'always' || enablementConfig === 'textEditors'; + const disableLightbulb = !enabled || editorUri?.scheme !== Schemas.untitled || !existingId; + + if (disableLightbulb || !editorUri) { + this._combinedEntry?.dispose(); + this._combinedEntry = undefined; + } else { + const lang = await this._languageDetectionService.detectLanguage(editorUri); + const skip: Record = { 'jsonc': 'json' }; + const existing = editorModel.getLanguageId(); + if (lang && lang !== existing && skip[existing] !== lang) { + const detectedName = this._languageService.getLanguageName(lang) || lang; + let tooltip = localize('status.autoDetectLanguage', "Accept Detected Language: {0}", detectedName); + const keybinding = this._keybindingService.lookupKeybinding(detectLanguageCommandId); + const label = keybinding?.getLabel(); + if (label) { + tooltip += ` (${label})`; + } + + const props: IStatusbarEntry = { + name: localize('langDetection.name', "Language Detection"), + ariaLabel: localize('langDetection.aria', "Change to Detected Language: {0}", lang), + tooltip, + command: detectLanguageCommandId, + text: '$(lightbulb-autofix)', + }; + if (!this._combinedEntry) { + this._combinedEntry = this._statusBarService.addEntry(props, LanguageDetectionStatusContribution._id, StatusbarAlignment.RIGHT, { id: 'status.editor.mode', alignment: StatusbarAlignment.RIGHT, compact: true }); + } else { + this._combinedEntry.update(props); + } + } else { + this._combinedEntry?.dispose(); + this._combinedEntry = undefined; + } + } + } +} + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LanguageDetectionStatusContribution, LifecyclePhase.Restored); + + +registerAction2(class extends Action2 { + + constructor() { + super({ + id: detectLanguageCommandId, + title: localize('detectlang', 'Detect Language from Content'), + f1: true, + precondition: ContextKeyExpr.and(NOTEBOOK_EDITOR_EDITABLE.toNegated(), EditorContextKeys.editorTextFocus), + keybinding: { primary: KeyCode.KeyE | KeyMod.CtrlCmd, weight: KeybindingWeight.WorkbenchContrib } + }); + } + + async run(accessor: ServicesAccessor): Promise { + const editorService = accessor.get(IEditorService); + const languageDetectionService = accessor.get(ILanguageDetectionService); + const editor = getCodeEditor(editorService.activeTextEditorControl); + const notificationService = accessor.get(INotificationService); + const editorUri = editor?.getModel()?.uri; + if (editorUri) { + const lang = await languageDetectionService.detectLanguage(editorUri); + if (lang) { + editor.getModel()?.setMode(lang); + } else { + notificationService.warn(localize('noDetection', "Unable to detect editor language")); + } + } + } +}); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts index f1834e4ee96..3747a011ae7 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts @@ -3,18 +3,23 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Delayer } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { localize } from 'vs/nls'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; -import { CHANGE_CELL_LANGUAGE } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CHANGE_CELL_LANGUAGE, DETECT_CELL_LANGUAGE } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; import { CellKind, CellStatusbarAlignment, INotebookCellStatusBarItem, INotebookCellStatusBarItemList, INotebookCellStatusBarItemProvider } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; +import { ILanguageDetectionService } from 'vs/workbench/services/languageDetection/common/languageDetectionWorkerService'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; class CellStatusBarLanguagePickerProvider implements INotebookCellStatusBarItemProvider { @@ -50,6 +55,72 @@ class CellStatusBarLanguagePickerProvider implements INotebookCellStatusBarItemP } } +class CellStatusBarLanguageDetectionProvider implements INotebookCellStatusBarItemProvider { + + readonly viewType = '*'; + + private delayer = new Delayer(500); + + constructor( + @INotebookService private readonly _notebookService: INotebookService, + @INotebookKernelService private readonly _notebookKernelService: INotebookKernelService, + @ILanguageService private readonly _languageService: ILanguageService, + @IConfigurationService private readonly _configurationService: IConfigurationService, + @ILanguageDetectionService private readonly _languageDetectionService: ILanguageDetectionService, + @IKeybindingService private readonly _keybindingService: IKeybindingService, + ) { } + + async provideCellStatusBarItems(uri: URI, index: number, token: CancellationToken): Promise { + return await this.delayer.trigger(async () => { + const doc = this._notebookService.getNotebookTextModel(uri); + const cell = doc?.cells[index]; + if (!cell || token.isCancellationRequested) { + return; + } + + const enablementConfig = this._configurationService.getValue('workbench.editor.languageDetectionHints'); + const enabled = enablementConfig === 'always' || enablementConfig === 'notebookEditors'; + if (!enabled) { + return; + } + + const currentLanguageId = cell.cellKind === CellKind.Markup ? + 'markdown' : + (this._languageService.getLanguageIdByLanguageName(cell.language) || cell.language); + + const kernel = this._notebookKernelService.getMatchingKernel(doc); + const items: INotebookCellStatusBarItem[] = []; + + if (kernel.selected) { + const availableLangs = []; + availableLangs.push(...kernel.selected.supportedLanguages, 'markdown'); + const detectedLanguageId = await this._languageDetectionService.detectLanguage(cell.uri, availableLangs); + + if (detectedLanguageId && currentLanguageId !== detectedLanguageId) { + const detectedName = this._languageService.getLanguageName(detectedLanguageId) || detectedLanguageId; + let tooltip = localize('notebook.cell.status.autoDetectLanguage', "Accept Detected Language: {0}", detectedName); + const keybinding = this._keybindingService.lookupKeybinding(DETECT_CELL_LANGUAGE); + const label = keybinding?.getLabel(); + if (label) { + tooltip += ` (${label})`; + } + + items.push({ + text: '$(lightbulb-autofix)', + command: DETECT_CELL_LANGUAGE, + tooltip, + alignment: CellStatusbarAlignment.Right, + priority: -Number.MAX_SAFE_INTEGER + 1 + }); + } + } + + return { items }; + }); + + } +} + class BuiltinCellStatusBarProviders extends Disposable { constructor( @IInstantiationService instantiationService: IInstantiationService, @@ -58,6 +129,7 @@ class BuiltinCellStatusBarProviders extends Disposable { const builtinProviders = [ CellStatusBarLanguagePickerProvider, + CellStatusBarLanguageDetectionProvider, ]; builtinProviders.forEach(p => { this._register(notebookCellStatusBarService.registerCellStatusBarItemProvider(instantiationService.createInstance(p))); diff --git a/src/vs/workbench/contrib/notebook/browser/controller/editActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/editActions.ts index 588675f6403..27a1c5b7c21 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/editActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/editActions.ts @@ -21,12 +21,13 @@ import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/ import { changeCellToKind, runDeleteAction } from 'vs/workbench/contrib/notebook/browser/controller/cellOperations'; import { CellToolbarOrder, CELL_TITLE_CELL_GROUP_ID, CELL_TITLE_OUTPUT_GROUP_ID, executeNotebookCondition, INotebookActionContext, INotebookCellActionContext, NotebookAction, NotebookCellAction, NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_HAS_OUTPUTS, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_USE_CONSOLIDATED_OUTPUT_BUTTON } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; -import { CellEditState, CHANGE_CELL_LANGUAGE, QUIT_EDIT_CELL_COMMAND_ID } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellEditState, CHANGE_CELL_LANGUAGE, DETECT_CELL_LANGUAGE, QUIT_EDIT_CELL_COMMAND_ID } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import * as icons from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { CellEditType, CellKind, ICellEditOperation, NotebookCellExecutionState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { ILanguageDetectionService } from 'vs/workbench/services/languageDetection/common/languageDetectionWorkerService'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; +import { INotificationService } from 'vs/platform/notification/common/notification'; const CLEAR_ALL_CELLS_OUTPUTS_COMMAND_ID = 'notebook.clearAllCellsOutputs'; const EDIT_CELL_COMMAND_ID = 'notebook.cell.edit'; @@ -437,23 +438,7 @@ registerAction2(class ChangeCellLanguageAction extends NotebookCellAction undefined, undefined, true - ); - } + await setCellToLanguage(languageId, context); } /** @@ -478,3 +463,48 @@ registerAction2(class ChangeCellLanguageAction extends NotebookCellAction { + const languageDetectionService = accessor.get(ILanguageDetectionService); + const notificationService = accessor.get(INotificationService); + const providerLanguages = [...context.notebookEditor.activeKernel?.supportedLanguages ?? []]; + providerLanguages.push('markdown'); + const detection = await languageDetectionService.detectLanguage(context.cell.uri, providerLanguages); + if (detection) { + setCellToLanguage(detection, context); + } else { + notificationService.warn(localize('noDetection', "Unable to detect cell language")); + } + } +}); + +async function setCellToLanguage(languageId: string, context: IChangeCellContext) { + if (languageId === 'markdown' && context.cell?.language !== 'markdown') { + const idx = context.notebookEditor.getCellIndex(context.cell); + await changeCellToKind(CellKind.Markup, { cell: context.cell, notebookEditor: context.notebookEditor, ui: true }, 'markdown', Mimes.markdown); + const newCell = context.notebookEditor.cellAt(idx); + + if (newCell) { + context.notebookEditor.focusNotebookCell(newCell, 'editor'); + } + } else if (languageId !== 'markdown' && context.cell?.cellKind === CellKind.Markup) { + await changeCellToKind(CellKind.Code, { cell: context.cell, notebookEditor: context.notebookEditor, ui: true }, languageId); + } else { + const index = context.notebookEditor.textModel.cells.indexOf(context.cell.model); + context.notebookEditor.textModel.applyEdits( + [{ editType: CellEditType.CellLanguage, index, language: languageId }], + true, undefined, () => undefined, undefined, true + ); + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 7153bcfe26c..34c9a2afe21 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -31,6 +31,7 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; //#region Shared commands export const EXPAND_CELL_INPUT_COMMAND_ID = 'notebook.cell.expandCellInput'; export const EXECUTE_CELL_COMMAND_ID = 'notebook.cell.execute'; +export const DETECT_CELL_LANGUAGE = 'notebook.cell.detectLanguage'; export const CHANGE_CELL_LANGUAGE = 'notebook.cell.changeLanguage'; export const QUIT_EDIT_CELL_COMMAND_ID = 'notebook.cell.quitEdit'; export const EXPAND_CELL_OUTPUT_COMMAND_ID = 'notebook.cell.expandCellOutput'; diff --git a/src/vs/workbench/services/languageDetection/browser/languageDetectionSimpleWorker.ts b/src/vs/workbench/services/languageDetection/browser/languageDetectionSimpleWorker.ts index 2d9166e9ed5..59d8bf7ef8b 100644 --- a/src/vs/workbench/services/languageDetection/browser/languageDetectionSimpleWorker.ts +++ b/src/vs/workbench/services/languageDetection/browser/languageDetectionSimpleWorker.ts @@ -9,7 +9,7 @@ import { IRequestHandler } from 'vs/base/common/worker/simpleWorker'; import { EditorSimpleWorker } from 'vs/editor/common/services/editorSimpleWorker'; import { IEditorWorkerHost } from 'vs/editor/common/services/editorWorkerHost'; -type RegexpModel = { detect: (inp: string, langBiases: Record) => string | undefined }; +type RegexpModel = { detect: (inp: string, langBiases: Record, supportedLangs?: string[]) => string | undefined }; /** * Called on the worker side @@ -34,7 +34,9 @@ export class LanguageDetectionSimpleWorker extends EditorSimpleWorker { private _modelOperations: ModelOperations | undefined; private _loadFailed: boolean = false; - public async detectLanguage(uri: string, langBiases: Record | undefined, preferHistory: boolean): Promise { + private modelIdToCoreId = new Map(); + + public async detectLanguage(uri: string, langBiases: Record | undefined, preferHistory: boolean, supportedLangs?: string[]): Promise { const languages: string[] = []; const confidences: number[] = []; const stopWatch = new StopWatch(true); @@ -43,8 +45,14 @@ export class LanguageDetectionSimpleWorker extends EditorSimpleWorker { const neuralResolver = async () => { for await (const language of this.detectLanguagesImpl(documentTextSample)) { - languages.push(language.languageId); - confidences.push(language.confidence); + if (!this.modelIdToCoreId.has(language.languageId)) { + this.modelIdToCoreId.set(language.languageId, await this._host.fhr('getLanguageId', [language.languageId])); + } + const coreId = this.modelIdToCoreId.get(language.languageId); + if (coreId && (!supportedLangs?.length || supportedLangs.includes(coreId))) { + languages.push(coreId); + confidences.push(language.confidence); + } } stopWatch.stop(); @@ -55,15 +63,7 @@ export class LanguageDetectionSimpleWorker extends EditorSimpleWorker { return undefined; }; - const historicalResolver = async () => { - if (langBiases) { - const regexpDetection = await this.runRegexpModel(documentTextSample, langBiases); - if (regexpDetection) { - return regexpDetection; - } - } - return undefined; - }; + const historicalResolver = async () => this.runRegexpModel(documentTextSample, langBiases ?? {}, supportedLangs); if (preferHistory) { const history = await historicalResolver(); @@ -112,11 +112,22 @@ export class LanguageDetectionSimpleWorker extends EditorSimpleWorker { } } - private async runRegexpModel(content: string, langBiases: Record): Promise { + private async runRegexpModel(content: string, langBiases: Record, supportedLangs?: string[]): Promise { const regexpModel = await this.getRegexpModel(); if (!regexpModel) { return; } - const detected = regexpModel.detect(content, langBiases); + if (supportedLangs?.length) { + // When using supportedLangs, normally computed biases are too extreme. Just use a "bitmask" of sorts. + for (const lang of Object.keys(langBiases)) { + if (supportedLangs.includes(lang)) { + langBiases[lang] = 1; + } else { + langBiases[lang] = 0; + } + } + } + + const detected = regexpModel.detect(content, langBiases, supportedLangs); return detected; } @@ -156,21 +167,21 @@ export class LanguageDetectionSimpleWorker extends EditorSimpleWorker { // For the following languages, we increase the confidence because // these are commonly used languages in VS Code and supported // by the model. - case 'javascript': + case 'js': case 'html': case 'json': - case 'typescript': + case 'ts': case 'css': - case 'python': + case 'py': case 'xml': case 'php': modelResult.confidence += LanguageDetectionSimpleWorker.positiveConfidenceCorrectionBucket1; break; // case 'yaml': // YAML has been know to cause incorrect language detection because the language is pretty simple. We don't want to increase the confidence for this. case 'cpp': - case 'shellscript': + case 'sh': case 'java': - case 'csharp': + case 'cs': case 'c': modelResult.confidence += LanguageDetectionSimpleWorker.positiveConfidenceCorrectionBucket2; break; diff --git a/src/vs/workbench/services/languageDetection/browser/languageDetectionWorkerServiceImpl.ts b/src/vs/workbench/services/languageDetection/browser/languageDetectionWorkerServiceImpl.ts index eaa283b67d3..9db520ddcb0 100644 --- a/src/vs/workbench/services/languageDetection/browser/languageDetectionWorkerServiceImpl.ts +++ b/src/vs/workbench/services/languageDetection/browser/languageDetectionWorkerServiceImpl.ts @@ -53,7 +53,7 @@ export class LanguageDetectionService extends Disposable implements ILanguageDet constructor( @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, - @ILanguageService private readonly _languageService: ILanguageService, + @ILanguageService languageService: ILanguageService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IDiagnosticsService private readonly _diagnosticsService: IDiagnosticsService, @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, @@ -68,6 +68,7 @@ export class LanguageDetectionService extends Disposable implements ILanguageDet this._languageDetectionWorkerClient = new LanguageDetectionWorkerClient( modelService, + languageService, telemetryService, // TODO: See if it's possible to bundle vscode-languagedetection this._environmentService.isBuilt && !isWeb @@ -95,7 +96,7 @@ export class LanguageDetectionService extends Disposable implements ILanguageDet let count = 0; for (const ext of fileExtensions.extensions) { - const langId = this.getLanguageId(ext); + const langId = this._languageDetectionWorkerClient.getLanguageId(ext); if (langId && count < TOP_LANG_COUNTS) { this.workspaceLanguageIds.add(langId); count++; @@ -109,15 +110,6 @@ export class LanguageDetectionService extends Disposable implements ILanguageDet return !!languageId && this._configurationService.getValue(LanguageDetectionService.enablementSettingKey, { overrideIdentifier: languageId }); } - private getLanguageId(language: string | undefined): string | undefined { - if (!language) { - return undefined; - } - if (this._languageService.isRegisteredLanguageId(language)) { - return language; - } - return this._languageService.guessLanguageIdByFilepathOrFirstLine(URI.file(`file.${language}`)) ?? undefined; - } private getLanguageBiases(): Record { if (!this.dirtyBiases) { return this.langBiases; } @@ -147,19 +139,14 @@ export class LanguageDetectionService extends Disposable implements ILanguageDet return biases; } - async detectLanguage(resource: URI): Promise { + async detectLanguage(resource: URI, supportedLangs?: string[]): Promise { const useHistory = this._configurationService.getValue(LanguageDetectionService.historyBasedEnablementConfig); const preferHistory = this._configurationService.getValue(LanguageDetectionService.preferHistoryConfig); if (useHistory) { await this.resolveWorkspaceLanguageIds(); } const biases = useHistory ? this.getLanguageBiases() : undefined; - const language = await this._languageDetectionWorkerClient.detectLanguage(resource, biases, preferHistory); - - if (language) { - return this.getLanguageId(language); - } - return undefined; + return this._languageDetectionWorkerClient.detectLanguage(resource, biases, preferHistory, supportedLangs); } private initEditorOpenedListeners(storageService: IStorageService) { @@ -234,6 +221,7 @@ export class LanguageDetectionWorkerClient extends EditorWorkerClient { constructor( modelService: IModelService, + private readonly _languageService: ILanguageService, private readonly _telemetryService: ITelemetryService, private readonly _indexJsUri: string, private readonly _modelJsonUri: string, @@ -260,6 +248,14 @@ export class LanguageDetectionWorkerClient extends EditorWorkerClient { return this.workerPromise; } + private _guessLanguageIdByUri(uri: URI): string | undefined { + const guess = this._languageService.guessLanguageIdByFilepathOrFirstLine(uri); + if (guess && guess !== 'unknown') { + return guess; + } + return undefined; + } + override async _getProxy(): Promise { return (await this._getOrCreateLanguageDetectionWorker()).getProxyObject(); } @@ -275,6 +271,8 @@ export class LanguageDetectionWorkerClient extends EditorWorkerClient { return this.getWeightsUri(); case 'getRegexpModelUri': return this.getRegexpModelUri(); + case 'getLanguageId': + return this.getLanguageId(args[0]); case 'sendTelemetryEvent': return this.sendTelemetryEvent(args[0], args[1], args[2]); default: @@ -286,6 +284,20 @@ export class LanguageDetectionWorkerClient extends EditorWorkerClient { return this._indexJsUri; } + getLanguageId(languageIdOrExt: string | undefined) { + if (!languageIdOrExt) { + return undefined; + } + if (this._languageService.isRegisteredLanguageId(languageIdOrExt)) { + return languageIdOrExt; + } + const guessed = this._guessLanguageIdByUri(URI.file(`file.${languageIdOrExt}`)); + if (!guessed || guessed === 'unknown') { + return undefined; + } + return guessed; + } + async getModelJsonUri() { return this._modelJsonUri; } @@ -306,9 +318,15 @@ export class LanguageDetectionWorkerClient extends EditorWorkerClient { }); } - public async detectLanguage(resource: URI, langBiases: Record | undefined, preferHistory: boolean): Promise { + public async detectLanguage(resource: URI, langBiases: Record | undefined, preferHistory: boolean, supportedLangs?: string[]): Promise { + const quickGuess = this._guessLanguageIdByUri(resource); + if (quickGuess) { + return quickGuess; + } + await this._withSyncedResources([resource]); - return (await this._getProxy()).detectLanguage(resource.toString(), langBiases, preferHistory); + const modelId = await (await this._getProxy()).detectLanguage(resource.toString(), langBiases, preferHistory, supportedLangs); + return this.getLanguageId(modelId); } } diff --git a/src/vs/workbench/services/languageDetection/common/languageDetectionWorkerService.ts b/src/vs/workbench/services/languageDetection/common/languageDetectionWorkerService.ts index 2b725466178..0fe24c9c6fd 100644 --- a/src/vs/workbench/services/languageDetection/common/languageDetectionWorkerService.ts +++ b/src/vs/workbench/services/languageDetection/common/languageDetectionWorkerService.ts @@ -19,9 +19,10 @@ export interface ILanguageDetectionService { /** * @param resource The resource to detect the language for. + * @param supportedLangs Optional. When populated, the model will only return languages from the provided list * @returns the language id for the given resource or undefined if the model is not confident enough. */ - detectLanguage(resource: URI): Promise; + detectLanguage(resource: URI, supportedLangs?: string[]): Promise; } //#region Telemetry events diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 295189cbf37..54fa8d93406 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -303,6 +303,9 @@ import 'vs/workbench/contrib/typeHierarchy/browser/typeHierarchy.contribution'; import 'vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsOutline'; import 'vs/workbench/contrib/outline/browser/outline.contribution'; +// Language Detection +import 'vs/workbench/contrib/languageDetection/browser/languageDetection.contribution'; + // Language Status import 'vs/workbench/contrib/languageStatus/browser/languageStatus.contribution'; From d1ef72c22ca975f012fbb913d49506cf89837f3c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 21 Apr 2022 07:10:49 +0200 Subject: [PATCH 194/245] :lipstick: dialog service in tests --- .../browser/abstractFileDialogService.ts | 19 ++++++++++++++++--- .../services/dialogs/common/dialogService.ts | 14 ++++++++++---- .../electron-sandbox/fileDialogService.ts | 6 ++++-- .../fileDialogService.test.ts | 6 ++++-- 4 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts b/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts index eb895e4de5e..1e4bcf88ab9 100644 --- a/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts @@ -30,6 +30,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { EditorOpenSource } from 'vs/platform/editor/common/editor'; +import { ILogService } from 'vs/platform/log/common/log'; export abstract class AbstractFileDialogService implements IFileDialogService { @@ -51,7 +52,8 @@ export abstract class AbstractFileDialogService implements IFileDialogService { @IPathService private readonly pathService: IPathService, @ICommandService protected readonly commandService: ICommandService, @IEditorService protected readonly editorService: IEditorService, - @ICodeEditorService protected readonly codeEditorService: ICodeEditorService + @ICodeEditorService protected readonly codeEditorService: ICodeEditorService, + @ILogService private readonly logService: ILogService ) { } async defaultFilePath(schemeFilter = this.getSchemeFilterForWindow()): Promise { @@ -113,13 +115,24 @@ export abstract class AbstractFileDialogService implements IFileDialogService { } async showSaveConfirm(fileNamesOrResources: (string | URI)[]): Promise { - if (this.environmentService.isExtensionDevelopment && this.environmentService.extensionTestsLocationURI) { - return ConfirmResult.DONT_SAVE; // no veto when we are in extension dev testing mode because we cannot assume we run interactive + if (this.skipDialogs()) { + this.logService.trace('FileDialogService: refused to show save confirmation dialog in tests.'); + + // no veto when we are in extension dev testing mode because we cannot assume we run interactive + return ConfirmResult.DONT_SAVE; } return this.doShowSaveConfirm(fileNamesOrResources); } + private skipDialogs(): boolean { + if (this.environmentService.isExtensionDevelopment && this.environmentService.extensionTestsLocationURI) { + return true; // integration tests + } + + return !!this.environmentService.enableSmokeTestDriver; // smoke tests + } + private async doShowSaveConfirm(fileNamesOrResources: (string | URI)[]): Promise { if (fileNamesOrResources.length === 0) { return ConfirmResult.DONT_SAVE; diff --git a/src/vs/workbench/services/dialogs/common/dialogService.ts b/src/vs/workbench/services/dialogs/common/dialogService.ts index a4bda254186..7360fe7d875 100644 --- a/src/vs/workbench/services/dialogs/common/dialogService.ts +++ b/src/vs/workbench/services/dialogs/common/dialogService.ts @@ -9,6 +9,7 @@ import { IConfirmation, IConfirmationResult, IDialogOptions, IDialogService, IIn import { DialogsModel } from 'vs/workbench/common/dialogs'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { ILogService } from 'vs/platform/log/common/log'; export class DialogService extends Disposable implements IDialogService { @@ -20,7 +21,10 @@ export class DialogService extends Disposable implements IDialogService { readonly onDidShowDialog = this.model.onDidShowDialog; - constructor(@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService) { + constructor( + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, + @ILogService private readonly logService: ILogService + ) { super(); } @@ -34,6 +38,8 @@ export class DialogService extends Disposable implements IDialogService { async confirm(confirmation: IConfirmation): Promise { if (this.skipDialogs()) { + this.logService.trace('DialogService: refused to show confirmation dialog in tests.'); + return { confirmed: true }; } @@ -44,7 +50,7 @@ export class DialogService extends Disposable implements IDialogService { async show(severity: Severity, message: string, buttons?: string[], options?: IDialogOptions): Promise { if (this.skipDialogs()) { - throw new Error('Dialogs are disabled in tests'); + throw new Error('DialogService: refused to show dialog in tests.'); } const handle = this.model.show({ showArgs: { severity, message, buttons, options } }); @@ -54,7 +60,7 @@ export class DialogService extends Disposable implements IDialogService { async input(severity: Severity, message: string, buttons: string[], inputs: IInput[], options?: IDialogOptions): Promise { if (this.skipDialogs()) { - throw new Error('Dialogs are disabled in tests'); + throw new Error('DialogService: refused to show input dialog in tests.'); } const handle = this.model.show({ inputArgs: { severity, message, buttons, inputs, options } }); @@ -64,7 +70,7 @@ export class DialogService extends Disposable implements IDialogService { async about(): Promise { if (this.skipDialogs()) { - throw new Error('Dialogs are disabled in tests'); + throw new Error('DialogService: refused to show about dialog in tests.'); } const handle = this.model.show({}); diff --git a/src/vs/workbench/services/dialogs/electron-sandbox/fileDialogService.ts b/src/vs/workbench/services/dialogs/electron-sandbox/fileDialogService.ts index e4468a73200..7d49ad0b2c3 100644 --- a/src/vs/workbench/services/dialogs/electron-sandbox/fileDialogService.ts +++ b/src/vs/workbench/services/dialogs/electron-sandbox/fileDialogService.ts @@ -25,6 +25,7 @@ import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ILogService } from 'vs/platform/log/common/log'; export class FileDialogService extends AbstractFileDialogService implements IFileDialogService { @@ -45,10 +46,11 @@ export class FileDialogService extends AbstractFileDialogService implements IFil @IPathService pathService: IPathService, @ICommandService commandService: ICommandService, @IEditorService editorService: IEditorService, - @ICodeEditorService codeEditorService: ICodeEditorService + @ICodeEditorService codeEditorService: ICodeEditorService, + @ILogService logService: ILogService ) { super(hostService, contextService, historyService, environmentService, instantiationService, - configurationService, fileService, openerService, dialogService, languageService, workspacesService, labelService, pathService, commandService, editorService, codeEditorService); + configurationService, fileService, openerService, dialogService, languageService, workspacesService, labelService, pathService, commandService, editorService, codeEditorService, logService); } private toNativeOpenDialogOptions(options: IPickAndOpenOptions): INativeOpenDialogOptions { diff --git a/src/vs/workbench/services/dialogs/test/electron-sandbox/fileDialogService.test.ts b/src/vs/workbench/services/dialogs/test/electron-sandbox/fileDialogService.test.ts index fbe3a518f48..0bea5b5eb37 100644 --- a/src/vs/workbench/services/dialogs/test/electron-sandbox/fileDialogService.test.ts +++ b/src/vs/workbench/services/dialogs/test/electron-sandbox/fileDialogService.test.ts @@ -33,6 +33,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ILogService } from 'vs/platform/log/common/log'; class TestFileDialogService extends FileDialogService { constructor( @@ -53,10 +54,11 @@ class TestFileDialogService extends FileDialogService { @IPathService pathService: IPathService, @ICommandService commandService: ICommandService, @IEditorService editorService: IEditorService, - @ICodeEditorService codeEditorService: ICodeEditorService + @ICodeEditorService codeEditorService: ICodeEditorService, + @ILogService logService: ILogService ) { super(hostService, contextService, historyService, environmentService, instantiationService, configurationService, fileService, - openerService, nativeHostService, dialogService, languageService, workspacesService, labelService, pathService, commandService, editorService, codeEditorService); + openerService, nativeHostService, dialogService, languageService, workspacesService, labelService, pathService, commandService, editorService, codeEditorService, logService); } protected override getSimpleFileDialog() { From 4875bf81b7d75e9ec3f9c8824d09225c6228bee2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 21 Apr 2022 07:24:00 +0200 Subject: [PATCH 195/245] editors - do not eat error action results --- src/vs/workbench/browser/parts/editor/editorPanes.ts | 10 +++++++--- .../browser/parts/editor/editorPlaceholder.ts | 12 ++++++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorPanes.ts b/src/vs/workbench/browser/parts/editor/editorPanes.ts index 9c47fb1d941..268c214a232 100644 --- a/src/vs/workbench/browser/parts/editor/editorPanes.ts +++ b/src/vs/workbench/browser/parts/editor/editorPanes.ts @@ -158,7 +158,7 @@ export class EditorPanes extends Disposable { } const buttons: string[] = []; - if (Array.isArray(errorActions) && errorActions.length > 0) { + if (errorActions && errorActions.length > 0) { for (const errorAction of errorActions) { buttons.push(errorAction.label); } @@ -183,10 +183,14 @@ export class EditorPanes extends Disposable { ); // Make sure to run any error action if present - if (result.choice !== cancelId && Array.isArray(errorActions)) { + if (result.choice !== cancelId && errorActions) { const errorAction = errorActions[result.choice]; if (errorAction) { - errorAction.run(); + const result = errorAction.run(); + if (result instanceof Promise) { + result.catch(error => this.dialogService.show(Severity.Error, toErrorMessage(error))); + } + errorHandled = true; // consider the error as handled! } } diff --git a/src/vs/workbench/browser/parts/editor/editorPlaceholder.ts b/src/vs/workbench/browser/parts/editor/editorPlaceholder.ts index 5e269c439a1..0464f14265b 100644 --- a/src/vs/workbench/browser/parts/editor/editorPlaceholder.ts +++ b/src/vs/workbench/browser/parts/editor/editorPlaceholder.ts @@ -5,6 +5,7 @@ import 'vs/css!./media/editorplaceholder'; import { localize } from 'vs/nls'; +import Severity from 'vs/base/common/severity'; import { IEditorOpenContext } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; @@ -28,6 +29,7 @@ import { editorErrorForeground, editorInfoForeground, editorWarningForeground } import { Codicon } from 'vs/base/common/codicons'; import { FileChangeType, FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files'; import { isErrorWithActions, toErrorMessage } from 'vs/base/common/errorMessage'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; export interface IEditorPlaceholderContents { icon: string; @@ -214,7 +216,8 @@ export class ErrorPlaceholderEditor extends EditorPlaceholder { @IThemeService themeService: IThemeService, @IStorageService storageService: IStorageService, @IInstantiationService instantiationService: IInstantiationService, - @IFileService private readonly fileService: IFileService + @IFileService private readonly fileService: IFileService, + @IDialogService private readonly dialogService: IDialogService ) { super(ErrorPlaceholderEditor.ID, telemetryService, themeService, storageService, instantiationService); } @@ -241,7 +244,12 @@ export class ErrorPlaceholderEditor extends EditorPlaceholder { actions = error.actions.map(action => { return { label: action.label, - run: () => action.run() + run: () => { + const result = action.run(); + if (result instanceof Promise) { + result.catch(error => this.dialogService.show(Severity.Error, toErrorMessage(error))); + } + } }; }); } else if (group) { From e43b54417f9780b6d470fd173060b308c3429e67 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 21 Apr 2022 07:50:16 +0200 Subject: [PATCH 196/245] Open Editors icon sometimes doesn't update when language is changed (fix #147733) --- src/vs/workbench/browser/labels.ts | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index e0b778dd82f..4cfe9f32bb5 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -272,14 +272,15 @@ class ResourceLabelWidget extends IconLabel { private readonly _onDidRender = this._register(new Emitter()); readonly onDidRender = this._onDidRender.event; - private label?: IResourceLabelProps; + private label: IResourceLabelProps | undefined = undefined; private decoration = this._register(new MutableDisposable()); - private options?: IResourceLabelOptions; - private computedIconClasses?: string[]; - private lastKnownDetectedLanguageId?: string; - private computedPathLabel?: string; + private options: IResourceLabelOptions | undefined = undefined; - private needsRedraw?: Redraw; + private computedIconClasses: string[] | undefined = undefined; + private computedLanguageId: string | undefined = undefined; + private computedPathLabel: string | undefined = undefined; + + private needsRedraw: Redraw | undefined = undefined; private isHidden: boolean = false; constructor( @@ -325,8 +326,8 @@ class ResourceLabelWidget extends IconLabel { } if (isEqual(model.uri, resource)) { - if (this.lastKnownDetectedLanguageId !== model.getLanguageId()) { - this.lastKnownDetectedLanguageId = model.getLanguageId(); + if (this.computedLanguageId !== model.getLanguageId()) { + this.computedLanguageId = model.getLanguageId(); this.render({ updateIcon: true, updateDecoration: false }); // update if the language id of the model has changed from our last known state } } @@ -444,8 +445,12 @@ class ResourceLabelWidget extends IconLabel { this.label = label; this.options = options; + if (hasResourceChanged) { + this.computedLanguageId = undefined; // reset computed language since resource changed + } + if (hasPathLabelChanged) { - this.computedPathLabel = undefined; // reset path label due to resource change + this.computedPathLabel = undefined; // reset path label due to resource/path-label change } this.render({ @@ -485,7 +490,7 @@ class ResourceLabelWidget extends IconLabel { clear(): void { this.label = undefined; this.options = undefined; - this.lastKnownDetectedLanguageId = undefined; + this.computedLanguageId = undefined; this.computedIconClasses = undefined; this.computedPathLabel = undefined; @@ -594,7 +599,7 @@ class ResourceLabelWidget extends IconLabel { this.label = undefined; this.options = undefined; - this.lastKnownDetectedLanguageId = undefined; + this.computedLanguageId = undefined; this.computedIconClasses = undefined; this.computedPathLabel = undefined; } From 26f019d9193924a94e59b21000c7fcc92ea94966 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 21 Apr 2022 11:20:50 +0200 Subject: [PATCH 197/245] Comment API docs --- src/vscode-dts/vscode.d.ts | 2 +- src/vscode-dts/vscode.proposed.commentsResolvedState.d.ts | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index 94f66962244..af79db87126 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -14387,7 +14387,7 @@ declare module 'vscode' { /** * The range the comment thread is located within the document. The thread icon will be shown - * at the first line of the range. + * at the last line of the range. */ range: Range; diff --git a/src/vscode-dts/vscode.proposed.commentsResolvedState.d.ts b/src/vscode-dts/vscode.proposed.commentsResolvedState.d.ts index 53e2d1e2a09..95fb288dd24 100644 --- a/src/vscode-dts/vscode.proposed.commentsResolvedState.d.ts +++ b/src/vscode-dts/vscode.proposed.commentsResolvedState.d.ts @@ -7,13 +7,18 @@ declare module 'vscode' { // https://github.com/microsoft/vscode/issues/127473 + /** + * The state of a comment thread. + */ export enum CommentThreadState { Unresolved = 0, Resolved = 1 } - // TODO@API doc export interface CommentThread { + /** + * The optional state of a comment thread, which may affect how the comment is displayed. + */ state?: CommentThreadState; } } From 1ccee4ae21cd59fba7c3a8e7a7fef2ab3e822996 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 21 Apr 2022 13:28:26 +0200 Subject: [PATCH 198/245] Fixes #143484 --- .../browser/viewParts/indentGuides/indentGuides.ts | 4 ++-- src/vs/editor/common/config/editorOptions.ts | 13 ++++++++++--- src/vs/monaco.d.ts | 2 +- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts index 489de25caf0..68ac4e98056 100644 --- a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts +++ b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts @@ -190,7 +190,7 @@ export class IndentGuidesOverlay extends DynamicViewOverlay { let activeIndentEndLineNumber = 0; let activeIndentLevel = 0; - if (this._bracketPairGuideOptions.highlightActiveIndentation && activeCursorPosition) { + if (this._bracketPairGuideOptions.highlightActiveIndentation !== false && activeCursorPosition) { const activeIndentInfo = this._context.viewModel.getActiveIndentGuide(activeCursorPosition.lineNumber, visibleStartLineNumber, visibleEndLineNumber); activeIndentStartLineNumber = activeIndentInfo.startLineNumber; activeIndentEndLineNumber = activeIndentInfo.endLineNumber; @@ -213,7 +213,7 @@ export class IndentGuidesOverlay extends DynamicViewOverlay { const indentGuide = (indentLvl - 1) * indentSize + 1; const isActive = // Disable active indent guide if there are bracket guides. - bracketGuidesInLine.length === 0 && + (this._bracketPairGuideOptions.highlightActiveIndentation === 'always' || bracketGuidesInLine.length === 0) && activeIndentStartLineNumber <= lineNumber && lineNumber <= activeIndentEndLineNumber && indentLvl === activeIndentLevel; diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 6ebe163d68a..889bd36b2d8 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -3693,7 +3693,7 @@ export interface IGuidesOptions { * Enable highlighting of the active indent guide. * Defaults to true. */ - highlightActiveIndentation?: boolean; + highlightActiveIndentation?: boolean | 'always'; } /** @@ -3751,8 +3751,15 @@ class GuideOptions extends BaseEditorOption Date: Thu, 21 Apr 2022 14:43:13 +0200 Subject: [PATCH 199/245] fixes #147456 --- .../workbench/services/themes/browser/workbenchThemeService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts index 6f8986a8963..901f92d9f3e 100644 --- a/src/vs/workbench/services/themes/browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/browser/workbenchThemeService.ts @@ -129,7 +129,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.fileIconThemeWatcher = new ThemeFileWatcher(fileService, environmentService, this.reloadCurrentFileIconTheme.bind(this)); this.fileIconThemeRegistry = new ThemeRegistry(fileIconThemesExtPoint, FileIconThemeData.fromExtensionTheme, true, FileIconThemeData.noIconTheme); this.fileIconThemeLoader = new FileIconThemeLoader(extensionResourceLoaderService, languageService); - this.onFileIconThemeChange = new Emitter(); + this.onFileIconThemeChange = new Emitter({ leakWarningThreshold: 400 }); this.currentFileIconTheme = FileIconThemeData.createUnloadedTheme(''); this.fileIconThemeSequencer = new Sequencer(); From ad4470522ecd858cfaf53a87c2702d7a40946ba1 Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 21 Apr 2022 14:45:38 +0200 Subject: [PATCH 200/245] propose `extensions.allAcrossExtensionHosts` and related APIs, https://github.com/microsoft/vscode/issues/145307 --- .../workbench/api/common/extHost.api.impl.ts | 57 +++++++++++++++---- .../api/common/extHostExtensionService.ts | 17 ++++-- .../api/common/extHostRequireInterceptor.ts | 7 +-- .../api/node/extHostExtensionService.ts | 2 +- .../api/worker/extHostExtensionService.ts | 2 +- .../common/extensionsApiProposals.ts | 1 + .../vscode.proposed.extensionsAny.d.ts | 40 +++++++++++++ 7 files changed, 103 insertions(+), 23 deletions(-) create mode 100644 src/vscode-dts/vscode.proposed.extensionsAny.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 1096349abd5..fb5bf4da95a 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -95,8 +95,13 @@ import { checkProposedApiEnabled, isProposedApiEnabled } from 'vs/workbench/serv import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/contrib/debug/common/debug'; import { ExtHostNotebookProxyKernels } from 'vs/workbench/api/common/extHostNotebookProxyKernels'; +export interface IExtensionRegistries { + mine: ExtensionDescriptionRegistry; + all: ExtensionDescriptionRegistry; +} + export interface IExtensionApiFactory { - (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; + (extension: IExtensionDescription, extensionInfo: IExtensionRegistries, configProvider: ExtHostConfigProvider): typeof vscode; } /** @@ -196,7 +201,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I // Register API-ish commands ExtHostApiCommands.register(extHostCommands); - return function (extension: IExtensionDescription, extensionRegistry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode { + return function (extension: IExtensionDescription, extensionInfo: IExtensionRegistries, configProvider: ExtHostConfigProvider): typeof vscode { // Check document selectors for being overly generic. Technically this isn't a problem but // in practice many extensions say they support `fooLang` but need fs-access to do so. Those @@ -360,10 +365,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I Object.freeze(env); } - const extensionKind = initData.remote.isRemote - ? extHostTypes.ExtensionKind.Workspace - : extHostTypes.ExtensionKind.UI; - + // namespace: tests const tests: typeof vscode.tests = { createTestController(provider, label, refreshHandler?: (token: vscode.CancellationToken) => Thenable | void) { return extHostTesting.createTestController(provider, label, refreshHandler); @@ -387,19 +389,50 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }; // namespace: extensions + const extensionKind = initData.remote.isRemote + ? extHostTypes.ExtensionKind.Workspace + : extHostTypes.ExtensionKind.UI; + const extensions: typeof vscode.extensions = { - getExtension(extensionId: string): vscode.Extension | undefined { - const desc = extensionRegistry.getExtensionDescription(extensionId); - if (desc) { - return new Extension(extensionService, extension.identifier, desc, extensionKind); + getExtension(extensionId: string, includeFromDifferentExtensionHosts?: boolean): vscode.Extension | undefined { + if (!isProposedApiEnabled(extension, 'extensionsAny')) { + includeFromDifferentExtensionHosts = false; + } + const mine = extensionInfo.mine.getExtensionDescription(extensionId); + if (mine) { + return new Extension(extensionService, extension.identifier, mine, extensionKind, false); + } + if (includeFromDifferentExtensionHosts) { + const foreign = extensionInfo.all.getExtensionDescription(extensionId); + if (foreign) { + return new Extension(extensionService, extension.identifier, foreign, extensionKind /* TODO@alexdima THIS IS WRONG */, true); + } } return undefined; }, get all(): vscode.Extension[] { - return extensionRegistry.getAllExtensionDescriptions().map((desc) => new Extension(extensionService, extension.identifier, desc, extensionKind)); + const result: vscode.Extension[] = []; + for (const desc of extensionInfo.mine.getAllExtensionDescriptions()) { + result.push(new Extension(extensionService, extension.identifier, desc, extensionKind, false)); + } + return result; + }, + get allAcrossExtensionHosts(): vscode.Extension[] { + checkProposedApiEnabled(extension, 'extensionsAny'); + const result: vscode.Extension[] = []; + for (const desc of extensionInfo.mine.getAllExtensionDescriptions()) { + result.push(new Extension(extensionService, extension.identifier, desc, extensionKind, false)); + } + for (const desc of extensionInfo.all.getAllExtensionDescriptions()) { + result.push(new Extension(extensionService, extension.identifier, desc, extensionKind /* TODO@alexdima THIS IS WRONG */, true)); + } + return result; }, get onDidChange() { - return extensionRegistry.onDidChange; + if (isProposedApiEnabled(extension, 'extensionsAny')) { + return Event.any(extensionInfo.mine.onDidChange, extensionInfo.all.onDidChange); + } + return extensionInfo.mine.onDidChange; } }; diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 101f6f4a95b..4b674cb4581 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -513,7 +513,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme get extensionMode() { return extensionMode; }, get extension() { if (extension === undefined) { - extension = new Extension(that, extensionDescription.identifier, extensionDescription, extensionKind); + extension = new Extension(that, extensionDescription.identifier, extensionDescription, extensionKind, false); } return extension; }, @@ -983,8 +983,9 @@ export class Extension implements vscode.Ex readonly extensionPath: string; readonly packageJSON: IExtensionDescription; readonly extensionKind: vscode.ExtensionKind; + readonly isFromDifferentExtensionHost: boolean; - constructor(extensionService: IExtHostExtensionService, originExtensionId: ExtensionIdentifier, description: IExtensionDescription, kind: ExtensionKind) { + constructor(extensionService: IExtHostExtensionService, originExtensionId: ExtensionIdentifier, description: IExtensionDescription, kind: ExtensionKind, isFromDifferentExtensionHost: boolean) { this.#extensionService = extensionService; this.#originExtensionId = originExtensionId; this.#identifier = description.identifier; @@ -993,21 +994,27 @@ export class Extension implements vscode.Ex this.extensionPath = path.normalize(originalFSPath(description.extensionLocation)); this.packageJSON = description; this.extensionKind = kind; + this.isFromDifferentExtensionHost = isFromDifferentExtensionHost; } get isActive(): boolean { + // TODO@alexdima support this return this.#extensionService.isActivated(this.#identifier); } get exports(): T { - if (this.packageJSON.api === 'none') { + if (this.packageJSON.api === 'none' || this.isFromDifferentExtensionHost) { return undefined!; // Strict nulloverride - Public api } return this.#extensionService.getExtensionExports(this.#identifier); } - activate(): Thenable { - return this.#extensionService.activateByIdWithErrors(this.#identifier, { startup: false, extensionId: this.#originExtensionId, activationEvent: 'api' }).then(() => this.exports); + async activate(): Promise { + if (this.isFromDifferentExtensionHost) { + throw new Error('Cannot activate foreign extension'); // TODO@alexdima support this + } + await this.#extensionService.activateByIdWithErrors(this.#identifier, { startup: false, extensionId: this.#originExtensionId, activationEvent: 'api' }); + return this.exports; } } diff --git a/src/vs/workbench/api/common/extHostRequireInterceptor.ts b/src/vs/workbench/api/common/extHostRequireInterceptor.ts index 459f32df428..4a072952392 100644 --- a/src/vs/workbench/api/common/extHostRequireInterceptor.ts +++ b/src/vs/workbench/api/common/extHostRequireInterceptor.ts @@ -8,10 +8,9 @@ import { URI } from 'vs/base/common/uri'; import { MainThreadTelemetryShape, MainContext } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostConfigProvider, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; -import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import * as vscode from 'vscode'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { IExtensionApiFactory } from 'vs/workbench/api/common/extHost.api.impl'; +import { IExtensionApiFactory, IExtensionRegistries } from 'vs/workbench/api/common/extHost.api.impl'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -41,7 +40,7 @@ export abstract class RequireInterceptor { constructor( private _apiFactory: IExtensionApiFactory, - private _extensionRegistry: ExtensionDescriptionRegistry, + private _extensionRegistry: IExtensionRegistries, @IInstantiationService private readonly _instaService: IInstantiationService, @IExtHostConfiguration private readonly _extHostConfiguration: IExtHostConfiguration, @IExtHostExtensionService private readonly _extHostExtensionService: IExtHostExtensionService, @@ -156,7 +155,7 @@ class VSCodeNodeModuleFactory implements INodeModuleFactory { constructor( private readonly _apiFactory: IExtensionApiFactory, private readonly _extensionPaths: ExtensionPaths, - private readonly _extensionRegistry: ExtensionDescriptionRegistry, + private readonly _extensionRegistry: IExtensionRegistries, private readonly _configProvider: ExtHostConfigProvider, private readonly _logService: ILogService, ) { diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 8521f23ca0a..62e0d6b4998 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -72,7 +72,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { } // Module loading tricks - const interceptor = this._instaService.createInstance(NodeModuleRequireInterceptor, extensionApiFactory, this._myRegistry); + const interceptor = this._instaService.createInstance(NodeModuleRequireInterceptor, extensionApiFactory, { mine: this._myRegistry, all: this._globalRegistry }); await interceptor.install(); performance.mark('code/extHost/didInitAPI'); diff --git a/src/vs/workbench/api/worker/extHostExtensionService.ts b/src/vs/workbench/api/worker/extHostExtensionService.ts index cc072dcdacd..a74fcfbc69b 100644 --- a/src/vs/workbench/api/worker/extHostExtensionService.ts +++ b/src/vs/workbench/api/worker/extHostExtensionService.ts @@ -44,7 +44,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService { // initialize API and register actors const apiFactory = this._instaService.invokeFunction(createApiFactoryAndRegisterActors); - this._fakeModules = this._instaService.createInstance(WorkerRequireInterceptor, apiFactory, this._myRegistry); + this._fakeModules = this._instaService.createInstance(WorkerRequireInterceptor, apiFactory, { mine: this._myRegistry, all: this._globalRegistry }); await this._fakeModules.install(); performance.mark('code/extHost/didInitAPI'); diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 074e8fbee5b..ea97e90e13f 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -19,6 +19,7 @@ export const allApiProposals = Object.freeze({ documentFiltersExclusive: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.documentFiltersExclusive.d.ts', editorInsets: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.editorInsets.d.ts', extensionRuntime: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.extensionRuntime.d.ts', + extensionsAny: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.extensionsAny.d.ts', externalUriOpener: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.externalUriOpener.d.ts', fileSearchProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.fileSearchProvider.d.ts', findTextInFiles: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.findTextInFiles.d.ts', diff --git a/src/vscode-dts/vscode.proposed.extensionsAny.d.ts b/src/vscode-dts/vscode.proposed.extensionsAny.d.ts new file mode 100644 index 00000000000..378e324fa3f --- /dev/null +++ b/src/vscode-dts/vscode.proposed.extensionsAny.d.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + // https://github.com/microsoft/vscode/issues/145307 + + export interface Extension { + + /** + * `true` when the extension is associated to another extension host. + * + * *Note* that an extension from another extension host cannot export + * API, e.g {@link Extension.exports its exports} are always `undefined`. + */ + readonly isFromDifferentExtensionHost: boolean; + } + + export namespace extensions { + + /** + * Get an extension by its full identifier in the form of: `publisher.name`. + * + * @param extensionId An extension identifier. + * @param includeDifferentExtensionHosts Include extensions from different extension host + * @return An extension or `undefined`. + */ + export function getExtension(extensionId: string, includeDifferentExtensionHosts: boolean): Extension | undefined; + + /** + * All extensions across all extension hosts. + * + * @see {@link Extension.isFromDifferentExtensionHost} + */ + export const allAcrossExtensionHosts: readonly Extension[]; + + } +} From cff4c7d59b16929a3ee96b8d5a67244906c25fc8 Mon Sep 17 00:00:00 2001 From: deepak1556 Date: Thu, 21 Apr 2022 22:48:49 +0900 Subject: [PATCH 201/245] chore: bump electron@17.4.1 --- .yarnrc | 2 +- cgmanifest.json | 4 ++-- package.json | 4 ++-- yarn.lock | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.yarnrc b/.yarnrc index 861ab1b26ec..6b1a011a28a 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,4 +1,4 @@ disturl "https://electronjs.org/headers" -target "17.4.0" +target "17.4.1" runtime "electron" build_from_source "true" diff --git a/cgmanifest.json b/cgmanifest.json index 31fc792caf7..1d2e0f72b7b 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -60,12 +60,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "0b031afcefa031c322843d5964e999768b95af0a" + "commitHash": "73c87bcfc6e18428c21676d68f829364e6a7b15d" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "17.4.0" + "version": "17.4.1" }, { "component": { diff --git a/package.json b/package.json index d612b999635..16fa1bcdd1b 100644 --- a/package.json +++ b/package.json @@ -135,7 +135,7 @@ "cssnano": "^4.1.11", "debounce": "^1.0.0", "deemon": "^1.4.0", - "electron": "17.4.0", + "electron": "17.4.1", "eslint": "8.7.0", "eslint-plugin-header": "3.1.1", "eslint-plugin-jsdoc": "^19.1.0", @@ -230,4 +230,4 @@ "elliptic": "^6.5.3", "nwmatcher": "^1.4.4" } -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index b298ec5a768..0e1a8164c4d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4296,10 +4296,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@17.4.0: - version "17.4.0" - resolved "https://registry.yarnpkg.com/electron/-/electron-17.4.0.tgz#9ad7b6bac92241dad5e8ab3e9f652f0ed3114859" - integrity sha512-eMuCOZMB9qsY63qzxEkyyqM09qs6mrbPBBDJJZgd8pnPWftE4zKmFp3B1vdHzjQ+1c1r/siigxbWTrpDNNri0A== +electron@17.4.1: + version "17.4.1" + resolved "https://registry.yarnpkg.com/electron/-/electron-17.4.1.tgz#203961ca7551553d6497a1ec61d5f85d64d7e2ef" + integrity sha512-0qX+DbiNXlVSUxXq4lWVTis8QYqC4Q7R/Xkk3YZQbHMXZ90bWilypC3gBZAcN4MQD4AYUIebphBOpRPxlXY3nQ== dependencies: "@electron/get" "^1.13.0" "@types/node" "^14.6.2" From 2fdb06ca285bf34c80e2425495677250b219fd2c Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Thu, 21 Apr 2022 10:17:48 -0400 Subject: [PATCH 202/245] Add minimum visible count to open editors (#147771) --- .../workbench/contrib/files/browser/files.contribution.ts | 8 +++++++- .../contrib/files/browser/views/openEditorsView.ts | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index 1a6f2c8f672..c113c495f27 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -337,10 +337,16 @@ configurationRegistry.registerConfiguration({ 'properties': { 'explorer.openEditors.visible': { 'type': 'number', - 'description': nls.localize({ key: 'openEditorsVisible', comment: ['Open is an adjective'] }, "Number of editors shown in the Open Editors pane. Setting this to 0 hides the Open Editors pane."), + 'description': nls.localize({ key: 'openEditorsVisible', comment: ['Open is an adjective'] }, "The maximum number of editors shown in the Open Editors pane. Setting this to 0 hides the Open Editors pane."), 'default': 9, 'minimum': 0 }, + 'explorer.openEditors.minVisible': { + 'type': 'number', + 'description': nls.localize({ key: 'openEditorsVisibleMin', comment: ['Open is an adjective'] }, "The minimum number of editor slots shown in the Open Editors pane. If set to 0 the Open Editors pane will dynamically resize based on the number of editors."), + 'default': 0, + 'minimum': 0 + }, 'explorer.openEditors.sortOrder': { 'type': 'string', 'enum': ['editorOrder', 'alphabetical', 'fullPath'], diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index b7bc23b3257..06ba5acdbde 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -59,6 +59,7 @@ const $ = dom.$; export class OpenEditorsView extends ViewPane { private static readonly DEFAULT_VISIBLE_OPEN_EDITORS = 9; + private static readonly DEFAULT_MIN_VISIBLE_OPEN_EDITORS = 0; static readonly ID = 'workbench.explorer.openEditorsView'; static readonly NAME = nls.localize({ key: 'openEditors', comment: ['Open is an adjective'] }, "Open Editors"); @@ -466,12 +467,17 @@ export class OpenEditorsView extends ViewPane { } private getMaxExpandedBodySize(): number { + let minVisibleOpenEditors = this.configurationService.getValue('explorer.openEditors.minVisible'); + // If it's not a number setting it to 0 will result in dynamic resizing. + if (typeof minVisibleOpenEditors !== 'number') { + minVisibleOpenEditors = OpenEditorsView.DEFAULT_MIN_VISIBLE_OPEN_EDITORS; + } const containerModel = this.viewDescriptorService.getViewContainerModel(this.viewDescriptorService.getViewContainerByViewId(this.id)!)!; if (containerModel.visibleViewDescriptors.length <= 1) { return Number.POSITIVE_INFINITY; } - return this.elementCount * OpenEditorsDelegate.ITEM_HEIGHT; + return (Math.max(this.elementCount, minVisibleOpenEditors)) * OpenEditorsDelegate.ITEM_HEIGHT; } private getMinExpandedBodySize(): number { From 2b3212ead039c28b81a1dc768ba9882002135453 Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 21 Apr 2022 16:54:11 +0200 Subject: [PATCH 203/245] enable proposal for integration API tests --- extensions/vscode-api-tests/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index f1086bf1ba4..00196d1fdec 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -13,6 +13,7 @@ "documentFiltersExclusive", "editorInsets", "extensionRuntime", + "extensionsAny", "externalUriOpener", "fileSearchProvider", "findTextInFiles", From aa69f3d7623c464aba726d12ea0d83428f43e8b9 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Thu, 21 Apr 2022 11:07:44 -0400 Subject: [PATCH 204/245] =?UTF-8?q?Fix=20#15178=20Finalize=20tabs=20API=20?= =?UTF-8?q?=F0=9F=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- extensions/git/src/repository.ts | 10 +- .../src/singlefolder-tests/window.test.ts | 84 ++--- .../workbench/api/common/extHost.api.impl.ts | 15 +- .../workbench/api/common/extHostEditorTabs.ts | 20 +- .../common/extHostVariableResolverService.ts | 8 +- .../test/browser/extHostEditorTabs.test.ts | 16 +- .../common/extensionsApiProposals.ts | 1 - src/vscode-dts/vscode.d.ts | 295 +++++++++++++++++- src/vscode-dts/vscode.proposed.tabs.d.ts | 284 ----------------- 9 files changed, 369 insertions(+), 364 deletions(-) delete mode 100644 src/vscode-dts/vscode.proposed.tabs.d.ts diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 1472c3e32b8..f762d54d47a 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -5,7 +5,7 @@ import * as fs from 'fs'; import * as path from 'path'; -import { CancellationToken, Command, Disposable, Event, EventEmitter, Memento, OutputChannel, ProgressLocation, ProgressOptions, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, ThemeColor, Uri, window, workspace, WorkspaceEdit, FileDecoration, commands, Tab, TabKindTextDiff, TabKindNotebookDiff, RelativePattern } from 'vscode'; +import { CancellationToken, Command, Disposable, Event, EventEmitter, Memento, OutputChannel, 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'; import { Branch, Change, ForcePushMode, GitErrorCodes, LogOptions, Ref, RefType, Remote, Status, CommitOptions, BranchQuery, FetchOptions } from './api/git'; @@ -1274,13 +1274,13 @@ export class Repository implements Disposable { const diffEditorTabsToClose: Tab[] = []; for (const tab of window.tabGroups.all.map(g => g.tabs).flat()) { - const { kind } = tab; - if (kind instanceof TabKindTextDiff || kind instanceof TabKindNotebookDiff) { - if (kind.modified.scheme === 'git' && indexResources.some(r => pathEquals(r, kind.modified.fsPath))) { + const { input } = tab; + if (input instanceof TabInputTextDiff || input instanceof TabInputNotebookDiff) { + if (input.modified.scheme === 'git' && indexResources.some(r => pathEquals(r, input.modified.fsPath))) { // Index diffEditorTabsToClose.push(tab); } - if (kind.modified.scheme === 'file' && kind.original.scheme === 'git' && workingTreeResources.some(r => pathEquals(r, kind.modified.fsPath))) { + if (input.modified.scheme === 'file' && input.original.scheme === 'git' && workingTreeResources.some(r => pathEquals(r, input.modified.fsPath))) { // Working Tree diffEditorTabsToClose.push(tab); } diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts index 463c78c40bb..5d004bb6db7 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { join } from 'path'; -import { CancellationTokenSource, commands, MarkdownString, TabKindNotebook, Position, QuickPickItem, Selection, StatusBarAlignment, TabKindTextDiff, TextEditor, TextEditorSelectionChangeKind, TextEditorViewColumnChangeEvent, TabKindText, Uri, ViewColumn, window, workspace } from 'vscode'; +import { CancellationTokenSource, commands, MarkdownString, TabInputNotebook, Position, QuickPickItem, Selection, StatusBarAlignment, TextEditor, TextEditorSelectionChangeKind, TextEditorViewColumnChangeEvent, TabInputText, Uri, ViewColumn, window, workspace, TabInputTextDiff } from 'vscode'; import { assertNoRpc, closeAllEditors, createRandomFile, pathEquals } from '../utils'; @@ -371,28 +371,28 @@ suite('vscode API - window', () => { }); //#region Tabs API tests - test('Tabs - move tab', async function () { - const [docA, docB, docC] = await Promise.all([ - workspace.openTextDocument(await createRandomFile()), - workspace.openTextDocument(await createRandomFile()), - workspace.openTextDocument(await createRandomFile()) - ]); + // test('Tabs - move tab', async function () { + // const [docA, docB, docC] = await Promise.all([ + // workspace.openTextDocument(await createRandomFile()), + // workspace.openTextDocument(await createRandomFile()), + // workspace.openTextDocument(await createRandomFile()) + // ]); - await window.showTextDocument(docA, { viewColumn: ViewColumn.One, preview: false }); - await window.showTextDocument(docB, { viewColumn: ViewColumn.One, preview: false }); - await window.showTextDocument(docC, { viewColumn: ViewColumn.Two, preview: false }); + // await window.showTextDocument(docA, { viewColumn: ViewColumn.One, preview: false }); + // await window.showTextDocument(docB, { viewColumn: ViewColumn.One, preview: false }); + // await window.showTextDocument(docC, { viewColumn: ViewColumn.Two, preview: false }); - const tabGroups = window.tabGroups; - assert.strictEqual(tabGroups.all.length, 2); + // const tabGroups = window.tabGroups; + // assert.strictEqual(tabGroups.all.length, 2); - const group1Tabs = tabGroups.all[0].tabs; - assert.strictEqual(group1Tabs.length, 2); + // const group1Tabs = tabGroups.all[0].tabs; + // assert.strictEqual(group1Tabs.length, 2); - const group2Tabs = tabGroups.all[1].tabs; - assert.strictEqual(group2Tabs.length, 1); + // const group2Tabs = tabGroups.all[1].tabs; + // assert.strictEqual(group2Tabs.length, 1); - await tabGroups.move(group1Tabs[0], ViewColumn.One, 1); - }); + // await tabGroups.move(group1Tabs[0], ViewColumn.One, 1); + // }); // TODO @lramos15 re-enable these once shape is more stable test('Tabs - vscode.open & vscode.diff', async function () { @@ -423,14 +423,14 @@ suite('vscode API - window', () => { const tabs = window.tabGroups.all.map(g => g.tabs).flat(1); assert.strictEqual(tabs.length, 5); - assert.ok(tabs[0].kind instanceof TabKindText); - assert.strictEqual(tabs[0].kind.uri.toString(), docA.uri.toString()); - assert.ok(tabs[1].kind instanceof TabKindText); - assert.strictEqual(tabs[1].kind.uri.toString(), docB.uri.toString()); - assert.ok(tabs[2].kind instanceof TabKindText); - assert.strictEqual(tabs[2].kind.uri.toString(), docC.uri.toString()); - assert.ok(tabs[3].kind instanceof TabKindText); - assert.strictEqual(tabs[3].kind.uri.toString(), commandFile.toString()); + assert.ok(tabs[0].input instanceof TabInputText); + assert.strictEqual(tabs[0].input.uri.toString(), docA.uri.toString()); + assert.ok(tabs[1].input instanceof TabInputText); + assert.strictEqual(tabs[1].input.uri.toString(), docB.uri.toString()); + assert.ok(tabs[2].input instanceof TabInputText); + assert.strictEqual(tabs[2].input.uri.toString(), docC.uri.toString()); + assert.ok(tabs[3].input instanceof TabInputText); + assert.strictEqual(tabs[3].input.uri.toString(), commandFile.toString()); }); test('Tabs - Ensure tabs getter is correct', async function () { @@ -459,17 +459,17 @@ suite('vscode API - window', () => { assert.strictEqual(tabs.length, 5); // All resources should match the text documents as they're the only tabs currently open - assert.ok(tabs[0].kind instanceof TabKindText); - assert.strictEqual(tabs[0].kind.uri.toString(), docA.uri.toString()); - assert.ok(tabs[1].kind instanceof TabKindNotebook); - assert.strictEqual(tabs[1].kind.uri.toString(), notebookDoc.uri.toString()); - assert.ok(tabs[2].kind instanceof TabKindText); - assert.strictEqual(tabs[2].kind.uri.toString(), docB.uri.toString()); - assert.ok(tabs[3].kind instanceof TabKindText); - assert.strictEqual(tabs[3].kind.uri.toString(), docC.uri.toString()); + assert.ok(tabs[0].input instanceof TabInputText); + assert.strictEqual(tabs[0].input.uri.toString(), docA.uri.toString()); + assert.ok(tabs[1].input instanceof TabInputNotebook); + assert.strictEqual(tabs[1].input.uri.toString(), notebookDoc.uri.toString()); + assert.ok(tabs[2].input instanceof TabInputText); + assert.strictEqual(tabs[2].input.uri.toString(), docB.uri.toString()); + assert.ok(tabs[3].input instanceof TabInputText); + assert.strictEqual(tabs[3].input.uri.toString(), docC.uri.toString()); // Diff editor and side by side editor report the right side as the resource - assert.ok(tabs[4].kind instanceof TabKindTextDiff); - assert.strictEqual(tabs[4].kind.modified.toString(), rightDiff.toString()); + assert.ok(tabs[4].input instanceof TabInputTextDiff); + assert.strictEqual(tabs[4].input.modified.toString(), rightDiff.toString()); assert.strictEqual(tabs[0].group.viewColumn, ViewColumn.One); assert.strictEqual(tabs[1].group.viewColumn, ViewColumn.One); @@ -495,20 +495,20 @@ suite('vscode API - window', () => { await window.showTextDocument(docA, { viewColumn: ViewColumn.One, preview: false }); let activeTab = getActiveTabInActiveGroup(); assert.ok(activeTab); - assert.ok(activeTab.kind instanceof TabKindText); - assert.strictEqual(activeTab.kind.uri.toString(), docA.uri.toString()); + assert.ok(activeTab.input instanceof TabInputText); + assert.strictEqual(activeTab.input.uri.toString(), docA.uri.toString()); await window.showTextDocument(docB, { viewColumn: ViewColumn.Two, preview: false }); activeTab = getActiveTabInActiveGroup(); assert.ok(activeTab); - assert.ok(activeTab.kind instanceof TabKindText); - assert.strictEqual(activeTab.kind.uri.toString(), docB.uri.toString()); + assert.ok(activeTab.input instanceof TabInputText); + assert.strictEqual(activeTab.input.uri.toString(), docB.uri.toString()); await window.showTextDocument(docC, { viewColumn: ViewColumn.Three, preview: false }); activeTab = getActiveTabInActiveGroup(); assert.ok(activeTab); - assert.ok(activeTab.kind instanceof TabKindText); - assert.strictEqual(activeTab.kind.uri.toString(), docC.uri.toString()); + assert.ok(activeTab.input instanceof TabInputText); + assert.strictEqual(activeTab.input.uri.toString(), docC.uri.toString()); await commands.executeCommand('workbench.action.closeActiveEditor'); await commands.executeCommand('workbench.action.closeActiveEditor'); diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index fb5bf4da95a..88ef2492dec 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -791,7 +791,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostUriOpeners.registerExternalUriOpener(extension.identifier, id, opener, metadata); }, get tabGroups(): vscode.TabGroups { - checkProposedApiEnabled(extension, 'tabs'); return extHostEditorTabs.tabGroups; }, getInlineCompletionItemController(provider: vscode.InlineCompletionItemProvider): vscode.InlineCompletionController { @@ -1345,13 +1344,13 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I LanguageStatusSeverity: extHostTypes.LanguageStatusSeverity, QuickPickItemKind: extHostTypes.QuickPickItemKind, InputBoxValidationSeverity: extHostTypes.InputBoxValidationSeverity, - TabKindText: extHostTypes.TextTabInput, - TabKindTextDiff: extHostTypes.TextDiffTabInput, - TabKindCustom: extHostTypes.CustomEditorTabInput, - TabKindNotebook: extHostTypes.NotebookEditorTabInput, - TabKindNotebookDiff: extHostTypes.NotebookDiffEditorTabInput, - TabKindWebview: extHostTypes.WebviewEditorTabInput, - TabKindTerminal: extHostTypes.TerminalEditorTabInput + TabInputText: extHostTypes.TextTabInput, + TabInputTextDiff: extHostTypes.TextDiffTabInput, + TabInputCustom: extHostTypes.CustomEditorTabInput, + TabInputNotebook: extHostTypes.NotebookEditorTabInput, + TabInputNotebookDiff: extHostTypes.NotebookDiffEditorTabInput, + TabInputWebview: extHostTypes.WebviewEditorTabInput, + TabInputTerminal: extHostTypes.TerminalEditorTabInput }; }; } diff --git a/src/vs/workbench/api/common/extHostEditorTabs.ts b/src/vs/workbench/api/common/extHostEditorTabs.ts index 6fe404878bb..1fbc2d487de 100644 --- a/src/vs/workbench/api/common/extHostEditorTabs.ts +++ b/src/vs/workbench/api/common/extHostEditorTabs.ts @@ -9,7 +9,7 @@ import { IEditorTabDto, IEditorTabGroupDto, IExtHostEditorTabsShape, MainContext import { URI } from 'vs/base/common/uri'; import { Emitter } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { CustomEditorTabInput, NotebookDiffEditorTabInput, NotebookEditorTabInput, TerminalEditorTabInput, TextDiffTabInput, TextTabInput, ViewColumn, WebviewEditorTabInput } from 'vs/workbench/api/common/extHostTypes'; +import { CustomEditorTabInput, NotebookDiffEditorTabInput, NotebookEditorTabInput, TerminalEditorTabInput, TextDiffTabInput, TextTabInput, WebviewEditorTabInput } from 'vs/workbench/api/common/extHostTypes'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { assertIsDefined } from 'vs/base/common/types'; import { diffSets } from 'vs/base/common/collections'; @@ -48,7 +48,7 @@ class ExtHostEditorTab { get label() { return that._dto.label; }, - get kind() { + get input() { return that._input; }, get isDirty() { @@ -256,14 +256,14 @@ export class ExtHostEditorTabs implements IExtHostEditorTabs { return this._closeTabs(tabsOrTabGroups as vscode.Tab[], preserveFocus); } }, - move: async (tab: vscode.Tab, viewColumn: ViewColumn, index: number, preservceFocus?: boolean) => { - const extHostTab = this._findExtHostTabFromApi(tab); - if (!extHostTab) { - throw new Error('Invalid tab'); - } - this._proxy.$moveTab(extHostTab.tabId, index, typeConverters.ViewColumn.from(viewColumn), preservceFocus); - return; - } + // move: async (tab: vscode.Tab, viewColumn: ViewColumn, index: number, preservceFocus?: boolean) => { + // const extHostTab = this._findExtHostTabFromApi(tab); + // if (!extHostTab) { + // throw new Error('Invalid tab'); + // } + // this._proxy.$moveTab(extHostTab.tabId, index, typeConverters.ViewColumn.from(viewColumn), preservceFocus); + // return; + // } }; this._apiObject = Object.freeze(obj); } diff --git a/src/vs/workbench/api/common/extHostVariableResolverService.ts b/src/vs/workbench/api/common/extHostVariableResolverService.ts index a992dd05f77..7455de15e22 100644 --- a/src/vs/workbench/api/common/extHostVariableResolverService.ts +++ b/src/vs/workbench/api/common/extHostVariableResolverService.ts @@ -50,10 +50,10 @@ class ExtHostVariableResolverService extends AbstractVariableResolverService { const activeTab = editorTabs.tabGroups.all.find(group => group.isActive)?.activeTab; if (activeTab !== undefined) { // Resolve a resource from the tab - if (activeTab.kind instanceof TextDiffTabInput || activeTab.kind instanceof NotebookDiffEditorTabInput) { - return activeTab.kind.modified; - } else if (activeTab.kind instanceof TextTabInput || activeTab.kind instanceof NotebookEditorTabInput || activeTab.kind instanceof CustomEditorTabInput) { - return activeTab.kind.uri; + if (activeTab.input instanceof TextDiffTabInput || activeTab.input instanceof NotebookDiffEditorTabInput) { + return activeTab.input.modified; + } else if (activeTab.input instanceof TextTabInput || activeTab.input instanceof NotebookEditorTabInput || activeTab.input instanceof CustomEditorTabInput) { + return activeTab.input.uri; } } } diff --git a/src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts b/src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts index a2076d386d2..1e385fc213f 100644 --- a/src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts +++ b/src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts @@ -229,10 +229,10 @@ suite('ExtHostEditorTabs', function () { let all = extHostEditorTabs.tabGroups.all.map(group => group.tabs).flat(); assert.strictEqual(all.length, 1); const apiTab1 = all[0]; - assert.ok(apiTab1.kind instanceof TextTabInput); + assert.ok(apiTab1.input instanceof TextTabInput); assert.strictEqual(tabDto.input.kind, TabInputKind.TextInput); const dtoResource = (tabDto.input as TextInputDto).uri; - assert.strictEqual(apiTab1.kind.uri.toString(), URI.revive(dtoResource).toString()); + assert.strictEqual(apiTab1.input.uri.toString(), URI.revive(dtoResource).toString()); assert.strictEqual(apiTab1.isDirty, true); @@ -250,8 +250,8 @@ suite('ExtHostEditorTabs', function () { all = extHostEditorTabs.tabGroups.all.map(group => group.tabs).flat(); assert.strictEqual(all.length, 1); const apiTab2 = all[0]; - assert.ok(apiTab1.kind instanceof TextTabInput); - assert.strictEqual(apiTab1.kind.uri.toString(), URI.revive(dtoResource).toString()); + assert.ok(apiTab1.input instanceof TextTabInput); + assert.strictEqual(apiTab1.input.uri.toString(), URI.revive(dtoResource).toString()); assert.strictEqual(apiTab2.isDirty, false); assert.strictEqual(apiTab1 === apiTab2, true); @@ -297,10 +297,10 @@ suite('ExtHostEditorTabs', function () { assert.strictEqual(all.length, 2); const activeTab1 = extHostEditorTabs.tabGroups.activeTabGroup?.activeTab; - assert.ok(activeTab1?.kind instanceof TextTabInput); + assert.ok(activeTab1?.input instanceof TextTabInput); assert.strictEqual(tabDtoAAA.input.kind, TabInputKind.TextInput); const dtoAAAResource = (tabDtoAAA.input as TextInputDto).uri; - assert.strictEqual(activeTab1?.kind?.uri.toString(), URI.revive(dtoAAAResource)?.toString()); + assert.strictEqual(activeTab1?.input?.uri.toString(), URI.revive(dtoAAAResource)?.toString()); assert.strictEqual(activeTab1?.isActive, true); extHostEditorTabs.$acceptTabOperation({ @@ -311,10 +311,10 @@ suite('ExtHostEditorTabs', function () { }); const activeTab2 = extHostEditorTabs.tabGroups.activeTabGroup?.activeTab; - assert.ok(activeTab2?.kind instanceof TextTabInput); + assert.ok(activeTab2?.input instanceof TextTabInput); assert.strictEqual(tabDtoBBB.input.kind, TabInputKind.TextInput); const dtoBBBResource = (tabDtoBBB.input as TextInputDto).uri; - assert.strictEqual(activeTab2?.kind?.uri.toString(), URI.revive(dtoBBBResource)?.toString()); + assert.strictEqual(activeTab2?.input?.uri.toString(), URI.revive(dtoBBBResource)?.toString()); assert.strictEqual(activeTab2?.isActive, true); assert.strictEqual(activeTab1?.isActive, false); }); diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index ea97e90e13f..62beb7707f3 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -48,7 +48,6 @@ export const allApiProposals = Object.freeze({ scmActionButton: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmActionButton.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', - tabs: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.tabs.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.d.ts b/src/vscode-dts/vscode.d.ts index af79db87126..e10e43d3dce 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -9044,6 +9044,11 @@ declare module 'vscode' { */ export namespace window { + /** + * Represents the grid widget within the main editor area + */ + export const tabGroups: TabGroups; + /** * The currently active editor or `undefined`. The active editor is the one * that currently has focus or, when none has focus, the one that has changed @@ -14640,8 +14645,6 @@ declare module 'vscode' { export function createCommentController(id: string, label: string): CommentController; } - //#endregion - /** * Represents a session of a currently logged in user. */ @@ -15447,6 +15450,294 @@ declare module 'vscode' { */ constructor(message: string | MarkdownString); } + + /** + * The tab represents a single text based resource + */ + export class TabInputText { + /** + * The uri represented by the tab. + */ + readonly uri: Uri; + /** + * Constructs a text tab input with the given URI. + * @param uri The URI of the tab. + */ + constructor(uri: Uri); + } + + /** + * The tab represents two text based resources + * being rendered as a diff. + */ + export class TabInputTextDiff { + /** + * The uri of the original text resource. + */ + readonly original: Uri; + /** + * The uri of the modified text resource. + */ + readonly modified: Uri; + /** + * Constructs a new text diff tab input with the given URIs. + * @param original The uri of the original text resource. + * @param modified The uri of the modified text resource. + */ + constructor(original: Uri, modified: Uri); + } + + /** + * The tab represents a custom editor. + */ + export class TabInputCustom { + /** + * The uri which the tab is representing. + */ + readonly uri: Uri; + /** + * The type of custom editor. + */ + readonly viewType: string; + /** + * Constructs a custom editor tab input + * @param uri The uri of the tab + * @param viewType The viewtpye of the custom editor + */ + constructor(uri: Uri, viewType: string); + } + + /** + * The tab represents a webview. + */ + export class TabInputWebview { + /** + * The type of webview. Maps to {@linkcode WebviewPanel.viewType WebviewPanel's viewType} + */ + readonly viewType: string; + /** + * Constructs a webview tab input with the given view type. + * @param viewType The type of webview. Maps to {@linkcode WebviewPanel.viewType WebviewPanel's viewType} + */ + constructor(viewType: string); + } + + /** + * The tab represents a notebook. + */ + export class TabInputNotebook { + /** + * The uri which the tab is representing. + */ + readonly uri: Uri; + /** + * The type of notebook. Maps to {@linkcode NotebookDocument.notebookType NotebookDocuments's notebookType} + */ + readonly notebookType: string; + /** + * Constructs a new tab input for a notebook. + * @param uri The uri of the notebook. + * @param notebookType The type of notebook. Maps to {@linkcode NotebookDocument.notebookType NotebookDocuments's notebookType} + */ + constructor(uri: Uri, notebookType: string); + } + + /** + * The tabs represents two notebooks in a diff configuration. + */ + export class TabInputNotebookDiff { + /** + * The uri of the original notebook. + */ + readonly original: Uri; + /** + * The uri of the modified notebook. + */ + readonly modified: Uri; + /** + * The type of notebook. Maps to {@linkcode NotebookDocument.notebookType NotebookDocuments's notebookType} + */ + readonly notebookType: string; + /** + * Constructs a notebook diff tab input. + * @param original The uri of the original unmodified notebook. + * @param modified The uri of the modified notebook. + * @param notebookType The type of notebook. Maps to {@linkcode NotebookDocument.notebookType NotebookDocuments's notebookType} + */ + constructor(original: Uri, modified: Uri, notebookType: string); + } + + /** + * The tab represents a terminal in the editor area. + */ + export class TabInputTerminal { + /** + * Constructs a terminal tab input. + */ + constructor(); + } + + /** + * Represents a tab within a {@link TabGroup group of tabs}. + * Tabs are merely the graphical representation within the editor area. + * A backing editor is not a guarantee. + */ + export interface Tab { + + /** + * The text displayed on the tab + */ + readonly label: string; + + /** + * The group which the tab belongs to + */ + readonly group: TabGroup; + + /** + * Defines the structure of the tab i.e. text, notebook, custom, etc. + * Resource and other useful properties are defined on the tab kind. + */ + readonly input: TabInputText | TabInputTextDiff | TabInputCustom | TabInputWebview | TabInputNotebook | TabInputNotebookDiff | TabInputTerminal | unknown; + + /** + * Whether or not the tab is currently active. + * This is dictated by being the selected tab in the group + */ + readonly isActive: boolean; + + /** + * Whether or not the dirty indicator is present on the tab + */ + readonly isDirty: boolean; + + /** + * Whether or not the tab is pinned (pin icon is present) + */ + readonly isPinned: boolean; + + /** + * Whether or not the tab is in preview mode. + */ + readonly isPreview: boolean; + } + + /** + * An event describing change to tabs. + */ + export interface TabChangeEvent { + /** + * The tabs that have been opened + */ + readonly opened: readonly Tab[]; + /** + * The tabs that have been closed + */ + readonly closed: readonly Tab[]; + /** + * Tabs that have changed, e.g have changed + * their {@link Tab.isActive active} state. + */ + readonly changed: readonly Tab[]; + } + + /** + * An event describing changes to tab groups. + */ + export interface TabGroupChangeEvent { + /** + * Tab groups that have been opened. + */ + readonly opened: readonly TabGroup[]; + /** + * Tab groups that have been closed. + */ + readonly closed: readonly TabGroup[]; + /** + * Tab groups that have changed, e.g have changed + * their {@link TabGroup.isActive active} state. + */ + readonly changed: readonly TabGroup[]; + } + + /** + * Represents a group of tabs. A tab group itself consists of multiple tabs. + */ + export interface TabGroup { + /** + * Whether or not the group is currently active. + * + * *Note* that only one tab group is active at a time, but that multiple tab + * groups can have an {@link TabGroup.aciveTab active tab}. + * + * @see {@link Tab.isActive} + */ + readonly isActive: boolean; + + /** + * The view column of the group + */ + readonly viewColumn: ViewColumn; + + /** + * The active {@link Tab tab} in the group. This is the tab which contents are currently + * being rendered. + * + * *Note* that there can be one active tab per group but there can only be one {@link TabGroups.activeTabGroup active group}. + */ + readonly activeTab: Tab | undefined; + + /** + * The list of tabs contained within the group. + * This can be empty if the group has no tabs open. + */ + readonly tabs: readonly Tab[]; + } + + /** + * Represents the main editor area which consists of multple groups which contain tabs. + */ + export interface TabGroups { + /** + * All the groups within the group container + */ + readonly all: readonly TabGroup[]; + + /** + * The currently active group + */ + readonly activeTabGroup: TabGroup; + + /** + * An {@link Event event} which fires when {@link TabGroup tab groups} have changed. + */ + readonly onDidChangeTabGroups: Event; + + /** + * An {@link Event event} which fires when {@link Tab tabs} have changed. + */ + readonly onDidChangeTabs: Event; + + /** + * Closes the tab. This makes the tab object invalid and the tab + * should no longer be used for further actions. + * Note: In the case of a dirty tab, a confirmation dialog will be shown which may be cancelled. If cancelled the tab is still valid + * + * @param tab The tab to close. + * @param preserveFocus When `true` focus will remain in its current position. If `false` it will jump to the next tab. + * @returns A promise that resolves to `true` when all tabs have been closed + */ + close(tab: Tab | readonly Tab[], preserveFocus?: boolean): Thenable; + + /** + * Closes the tab group. This makes the tab group object invalid and the tab group + * should no longer be used for furhter actions. + * @param tabGroup The tab group to close. + * @param preserveFocus When `true` focus will remain in its current position. + * @returns A promise that resolves to `true` when all tab groups have been closed + */ + close(tabGroup: TabGroup | readonly TabGroup[], preserveFocus?: boolean): Thenable; + } } /** diff --git a/src/vscode-dts/vscode.proposed.tabs.d.ts b/src/vscode-dts/vscode.proposed.tabs.d.ts deleted file mode 100644 index 6d1f6e9b00a..00000000000 --- a/src/vscode-dts/vscode.proposed.tabs.d.ts +++ /dev/null @@ -1,284 +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/15178 - - /** - * The tab represents a single text based resource - */ - export class TabKindText { - /** - * The uri represented by the tab. - */ - readonly uri: Uri; - constructor(uri: Uri); - } - - /** - * The tab represents two text based resources - * being rendered as a diff. - */ - export class TabKindTextDiff { - /** - * The uri of the original text resource. - */ - readonly original: Uri; - /** - * The uri of the modified text resource. - */ - readonly modified: Uri; - constructor(original: Uri, modified: Uri); - } - - /** - * The tab represents a custom editor. - */ - export class TabKindCustom { - /** - * The uri which the tab is representing. - */ - readonly uri: Uri; - /** - * The type of custom editor. - */ - readonly viewType: string; - constructor(uri: Uri, viewType: string); - } - - /** - * The tab represents a webview. - */ - export class TabKindWebview { - /** - * The type of webview. Maps to {@linkcode WebviewPanel.viewType WebviewPanel's viewType} - */ - readonly viewType: string; - constructor(viewType: string); - } - - /** - * The tab represents a notebook. - */ - export class TabKindNotebook { - /** - * The uri which the tab is representing. - */ - readonly uri: Uri; - /** - * The type of notebook. Maps to {@linkcode NotebookDocument.notebookType NotebookDocuments's notebookType} - */ - readonly notebookType: string; - constructor(uri: Uri, notebookType: string); - } - - /** - * The tabs represents two notebooks in a diff configuration. - */ - export class TabKindNotebookDiff { - /** - * The uri of the original notebook. - */ - readonly original: Uri; - /** - * The uri of the modified notebook. - */ - readonly modified: Uri; - /** - * The type of notebook. Maps to {@linkcode NotebookDocument.notebookType NotebookDocuments's notebookType} - */ - readonly notebookType: string; - constructor(original: Uri, modified: Uri, notebookType: string); - } - - /** - * The tab represents a terminal in the editor area. - */ - export class TabKindTerminal { - constructor(); - } - - /** - * Represents a tab within a {@link TabGroup group of tabs}. - * Tabs are merely the graphical representation within the editor area. - * A backing editor is not a guarantee. - */ - export interface Tab { - - /** - * The text displayed on the tab - */ - readonly label: string; - - /** - * The group which the tab belongs to - */ - readonly group: TabGroup; - - /** - * Defines the structure of the tab i.e. text, notebook, custom, etc. - * Resource and other useful properties are defined on the tab kind. - */ - readonly kind: TabKindText | TabKindTextDiff | TabKindCustom | TabKindWebview | TabKindNotebook | TabKindNotebookDiff | TabKindTerminal | unknown; - - /** - * Whether or not the tab is currently active. - * This is dictated by being the selected tab in the group - */ - readonly isActive: boolean; - - /** - * Whether or not the dirty indicator is present on the tab - */ - readonly isDirty: boolean; - - /** - * Whether or not the tab is pinned (pin icon is present) - */ - readonly isPinned: boolean; - - /** - * Whether or not the tab is in preview mode. - */ - readonly isPreview: boolean; - } - - export namespace window { - /** - * Represents the grid widget within the main editor area - */ - export const tabGroups: TabGroups; - } - - /** - * An event describing change to tabs. - */ - export interface TabChangeEvent { - /** - * The tabs that have been opened - */ - readonly opened: readonly Tab[]; - /** - * The tabs that have been closed - */ - readonly closed: readonly Tab[]; - /** - * Tabs that have changed, e.g have changed - * their {@link Tab.isActive active} state. - */ - readonly changed: readonly Tab[]; - } - - /** - * An event describing changes to tab groups. - */ - export interface TabGroupChangeEvent { - /** - * Tab groups that have been opened. - */ - readonly opened: readonly TabGroup[]; - /** - * Tab groups that have been closed. - */ - readonly closed: readonly TabGroup[]; - /** - * Tab groups that have changed, e.g have changed - * their {@link TabGroup.isActive active} state. - */ - readonly changed: readonly TabGroup[]; - } - - /** - * Represents a group of tabs. A tab group itself consists of multiple tabs. - */ - export interface TabGroup { - /** - * Whether or not the group is currently active. - * - * *Note* that only one tab group is active at a time, but that multiple tab - * groups can have an {@link TabGroup.aciveTab active tab}. - * - * @see {@link Tab.isActive} - */ - readonly isActive: boolean; - - /** - * The view column of the group - */ - readonly viewColumn: ViewColumn; - - /** - * The active {@link Tab tab} in the group. This is the tab which contents are currently - * being rendered. - * - * *Note* that there can be one active tab per group but there can only be one {@link TabGroups.activeTabGroup active group}. - */ - readonly activeTab: Tab | undefined; - - /** - * The list of tabs contained within the group. - * This can be empty if the group has no tabs open. - */ - readonly tabs: readonly Tab[]; - } - - /** - * Represents the main editor area which consists of multple groups which contain tabs. - */ - export interface TabGroups { - /** - * All the groups within the group container - */ - readonly all: readonly TabGroup[]; - - /** - * The currently active group - */ - readonly activeTabGroup: TabGroup; - - /** - * An {@link Event event} which fires when {@link TabGroup tab groups} have changed. - */ - readonly onDidChangeTabGroups: Event; - - /** - * An {@link Event event} which fires when {@link Tab tabs} have changed. - */ - readonly onDidChangeTabs: Event; - - /** - * Closes the tab. This makes the tab object invalid and the tab - * should no longer be used for further actions. - * Note: In the case of a dirty tab, a confirmation dialog will be shown which may be cancelled. If cancelled the tab is still valid - * - * @param tab The tab to close. - * @param preserveFocus When `true` focus will remain in its current position. If `false` it will jump to the next tab. - * @returns A promise that resolves to `true` when all tabs have been closed - */ - close(tab: Tab | readonly Tab[], preserveFocus?: boolean): Thenable; - - /** - * Closes the tab group. This makes the tab group object invalid and the tab group - * should no longer be used for furhter actions. - * @param tabGroup The tab group to close. - * @param preserveFocus When `true` focus will remain in its current position. - * @returns A promise that resolves to `true` when all tab groups have been closed - */ - close(tabGroup: TabGroup | readonly TabGroup[], preserveFocus?: boolean): Thenable; - - /** - * Moves a tab to the given index within the column. - * If the index is out of range, the tab will be moved to the end of the column. - * If the column is out of range, a new one will be created after the last existing column. - * - * @package tab The tab to move. - * @param viewColumn The column to move the tab into - * @param index The index to move the tab to - */ - // TODO@API remove for now - move(tab: Tab, viewColumn: ViewColumn, index: number, preserveFocus?: boolean): Thenable; - } -} From 08ac1bb67ca2459496b272d8f4a908757f24f56f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 21 Apr 2022 17:10:10 +0200 Subject: [PATCH 205/245] tests - improve exclusion handling for node.js tests --- test/unit/node/index.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/unit/node/index.js b/test/unit/node/index.js index 436eec5ed91..fd55cea9d93 100644 --- a/test/unit/node/index.js +++ b/test/unit/node/index.js @@ -25,11 +25,12 @@ const optimist = require('optimist') const TEST_GLOB = '**/test/**/*.test.js'; -const excludeGlob = '**/{browser,electron-sandbox,electron-browser,electron-main}/**/*.test.js'; -const excludeModules = [ - 'vs/platform/environment/test/node/nativeModules.test.js', // native modules are compiled against Electron and this test would fail with node.js - 'vs/base/parts/storage/test/node/storage.test.js', // same as above, due to direct dependency to sqlite native module - 'vs/workbench/contrib/testing/test/common/testResultService.test.js' // flaky (https://github.com/microsoft/vscode/issues/137853) + +const excludeGlobs = [ + '**/{browser,electron-sandbox,electron-browser,electron-main}/**/*.test.js', + '**/vs/platform/environment/test/node/nativeModules.test.js', // native modules are compiled against Electron and this test would fail with node.js + '**/vs/base/parts/storage/test/node/storage.test.js', // same as above, due to direct dependency to sqlite native module + '**/vs/workbench/contrib/testing/test/**' // flaky (https://github.com/microsoft/vscode/issues/137853) ]; /** @@ -152,7 +153,7 @@ function main() { /** @type {string[]} */ const modules = []; for (let file of files) { - if (!minimatch(file, excludeGlob) && excludeModules.indexOf(file) === -1) { + if (!excludeGlobs.some(excludeGlob => minimatch(file, excludeGlob))) { modules.push(file.replace(/\.js$/, '')); } } From 88e0a4a261cd0aafa1f574ca7c4427001c72fa0e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 21 Apr 2022 18:58:30 +0200 Subject: [PATCH 206/245] `playwright` causes issues with our `ipcMain` validation (part one of #147301) --- src/vs/base/parts/ipc/electron-main/ipcMain.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/vs/base/parts/ipc/electron-main/ipcMain.ts b/src/vs/base/parts/ipc/electron-main/ipcMain.ts index 6dd54fb22e3..95e8e4e1c32 100644 --- a/src/vs/base/parts/ipc/electron-main/ipcMain.ts +++ b/src/vs/base/parts/ipc/electron-main/ipcMain.ts @@ -109,9 +109,6 @@ class ValidatedIpcMain implements Event.NodeEventEmitter { } const sender = event.senderFrame; - if (!sender) { - return true; // happens when renderer uses `ipcRenderer.postMessage` (TODO@bpasero revisit with newer Electron version) - } const url = sender.url; if (!url) { From fb8dc11d586fe92633ac3342c8dc05aa49dc6070 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 21 Apr 2022 18:58:44 +0200 Subject: [PATCH 207/245] Failed IPC communication with `playwright` smoke tests from preload script (for #146785) --- test/automation/src/application.ts | 25 +------------- .../src/areas/extensions/extensions.test.ts | 19 +++++------ .../src/areas/workbench/localization.test.ts | 34 ++++++++----------- 3 files changed, 24 insertions(+), 54 deletions(-) diff --git a/test/automation/src/application.ts b/test/automation/src/application.ts index 675915bd26c..c65f1cfd701 100644 --- a/test/automation/src/application.ts +++ b/test/automation/src/application.ts @@ -115,7 +115,7 @@ export class Application { private async checkWindowReady(code: Code): Promise { // We need a rendered workbench - await this.checkWorkbenchReady(code); + await measureAndLog(code.waitForElement('.monaco-workbench'), 'Application#checkWindowReady: wait for .monaco-workbench element', this.logger); // Remote but not web: wait for a remote connection state change if (this.remote) { @@ -134,27 +134,4 @@ export class Application { }, 300 /* = 30s of retry */), 'Application#checkWindowReady: wait for remote indicator', this.logger); } } - - private async checkWorkbenchReady(code: Code): Promise { - - // Web / Legacy: just poll for workbench element - if (this.web) { - await measureAndLog(code.waitForElement('.monaco-workbench'), 'Application#checkWindowReady: wait for .monaco-workbench element', this.logger); - } - - // Desktop (playwright): we see hangs, where IPC messages - // are not delivered (https://github.com/microsoft/vscode/issues/146785) - // Workaround is to try to reload the window when that happens - else { - try { - await measureAndLog(code.waitForElement('.monaco-workbench', undefined, 100 /* 10s of retry */), 'Application#checkWindowReady: wait for .monaco-workbench element', this.logger); - } catch (error) { - this.logger.log(`checkWindowReady: giving up after 10s, reloading window and trying again...`); - - await code.driver.reload(); - - return this.checkWorkbenchReady(code); - } - } - } } diff --git a/test/smoke/src/areas/extensions/extensions.test.ts b/test/smoke/src/areas/extensions/extensions.test.ts index de5b4fa91c6..7a4875bcd7f 100644 --- a/test/smoke/src/areas/extensions/extensions.test.ts +++ b/test/smoke/src/areas/extensions/extensions.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Application, Logger } from '../../../../automation'; -import { installAllHandlers, retryWithRestart } from '../../utils'; +import { installAllHandlers } from '../../utils'; export function setup(logger: Logger) { describe('Extensions', () => { @@ -13,19 +13,16 @@ export function setup(logger: Logger) { installAllHandlers(logger); it('install and enable vscode-smoketest-check extension', async function () { - this.timeout(2 * 60 * 1000); // https://github.com/microsoft/vscode/issues/146800 - const app = this.app as Application; - await retryWithRestart(app, async () => { - await app.workbench.extensions.openExtensionsViewlet(); - await app.workbench.extensions.installExtension('ms-vscode.vscode-smoketest-check', true); - // Close extension editor because keybindings dispatch is not working when web views are opened and focused - // https://github.com/microsoft/vscode/issues/110276 - await app.workbench.extensions.closeExtension('vscode-smoketest-check'); + await app.workbench.extensions.openExtensionsViewlet(); + await app.workbench.extensions.installExtension('ms-vscode.vscode-smoketest-check', true); - await app.workbench.quickaccess.runCommand('Smoke Test Check'); - }); + // Close extension editor because keybindings dispatch is not working when web views are opened and focused + // https://github.com/microsoft/vscode/issues/110276 + await app.workbench.extensions.closeExtension('vscode-smoketest-check'); + + await app.workbench.quickaccess.runCommand('Smoke Test Check'); }); }); } diff --git a/test/smoke/src/areas/workbench/localization.test.ts b/test/smoke/src/areas/workbench/localization.test.ts index 2b8704b72ff..be81bb17d4c 100644 --- a/test/smoke/src/areas/workbench/localization.test.ts +++ b/test/smoke/src/areas/workbench/localization.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Logger, Application } from '../../../../automation'; -import { installAllHandlers, retryWithRestart } from '../../utils'; +import { installAllHandlers } from '../../utils'; export function setup(logger: Logger) { @@ -14,30 +14,26 @@ export function setup(logger: Logger) { installAllHandlers(logger); it('starts with "DE" locale and verifies title and viewlets text is in German', async function () { - this.timeout(2 * 60 * 1000); // https://github.com/microsoft/vscode/issues/146800 - const app = this.app as Application; - await retryWithRestart(app, async () => { - await app.workbench.extensions.openExtensionsViewlet(); - await app.workbench.extensions.installExtension('ms-ceintl.vscode-language-pack-de', false); - await app.restart({ extraArgs: ['--locale=DE'] }); + await app.workbench.extensions.openExtensionsViewlet(); + await app.workbench.extensions.installExtension('ms-ceintl.vscode-language-pack-de', false); + await app.restart({ extraArgs: ['--locale=DE'] }); - const result = await app.workbench.localization.getLocalizedStrings(); - const localeInfo = await app.workbench.localization.getLocaleInfo(); + const result = await app.workbench.localization.getLocalizedStrings(); + const localeInfo = await app.workbench.localization.getLocaleInfo(); - if (localeInfo.locale === undefined || localeInfo.locale.toLowerCase() !== 'de') { - throw new Error(`The requested locale for VS Code was not German. The received value is: ${localeInfo.locale === undefined ? 'not set' : localeInfo.locale}`); - } + if (localeInfo.locale === undefined || localeInfo.locale.toLowerCase() !== 'de') { + throw new Error(`The requested locale for VS Code was not German. The received value is: ${localeInfo.locale === undefined ? 'not set' : localeInfo.locale}`); + } - if (localeInfo.language.toLowerCase() !== 'de') { - throw new Error(`The UI language is not German. It is ${localeInfo.language}`); - } + if (localeInfo.language.toLowerCase() !== 'de') { + throw new Error(`The UI language is not German. It is ${localeInfo.language}`); + } - if (result.open.toLowerCase() !== 'öffnen' || result.close.toLowerCase() !== 'schließen' || result.find.toLowerCase() !== 'finden') { - throw new Error(`Received wrong German localized strings: ${JSON.stringify(result, undefined, 0)}`); - } - }); + if (result.open.toLowerCase() !== 'öffnen' || result.close.toLowerCase() !== 'schließen' || result.find.toLowerCase() !== 'finden') { + throw new Error(`Received wrong German localized strings: ${JSON.stringify(result, undefined, 0)}`); + } }); }); } From 70e50659a1b95bf355660b47389272ae4bbe008d Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 21 Apr 2022 21:59:33 +0300 Subject: [PATCH 208/245] Fixes #146759: If defined, use the line highlight color as the textarea IME background color --- src/vs/editor/common/core/editorColorRegistry.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/common/core/editorColorRegistry.ts b/src/vs/editor/common/core/editorColorRegistry.ts index e44ca26bea2..5460cb16e56 100644 --- a/src/vs/editor/common/core/editorColorRegistry.ts +++ b/src/vs/editor/common/core/editorColorRegistry.ts @@ -85,7 +85,13 @@ export const editorUnicodeHighlightBackground = registerColor('editorUnicodeHigh registerThemingParticipant((theme, collector) => { const background = theme.getColor(editorBackground); if (background) { - collector.addRule(`.monaco-editor, .monaco-editor-background, .monaco-editor .inputarea.ime-input { background-color: ${background}; }`); + collector.addRule(`.monaco-editor, .monaco-editor-background { background-color: ${background}; }`); + } + + const lineHighlight = theme.getColor(editorLineHighlight); + const imeBackground = (lineHighlight && !lineHighlight.isTransparent() ? lineHighlight : background); + if (imeBackground) { + collector.addRule(`.monaco-editor .inputarea.ime-input { background-color: ${imeBackground}; }`); } const foreground = theme.getColor(editorForeground); From 936ce0b267f06ce9b6fdc9e2c745f9a0fe8a5312 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Thu, 21 Apr 2022 13:01:46 -0700 Subject: [PATCH 209/245] re #147183. Support editor options in IDefaultEditor (#147816) * re #147183. Support editor options in IDefaultEditor * Add deprecation tag. * Avoid type casing. * :lipstick: --- src/vs/platform/window/common/window.ts | 16 ++++++++++++++-- src/vs/workbench/browser/layout.ts | 3 ++- src/vs/workbench/browser/web.api.ts | 8 ++++++++ src/vs/workbench/common/editor.ts | 9 ++++++--- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/vs/platform/window/common/window.ts b/src/vs/platform/window/common/window.ts index 9fd60d23a45..dfa18a3fcda 100644 --- a/src/vs/platform/window/common/window.ts +++ b/src/vs/platform/window/common/window.ts @@ -8,6 +8,7 @@ import { isLinux, isMacintosh, isNative, isWeb } from 'vs/base/common/platform'; 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'; +import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { FileType } from 'vs/platform/files/common/files'; import { LogLevel } from 'vs/platform/log/common/log'; @@ -178,8 +179,15 @@ export interface IPathData { // the file path to open within the instance readonly fileUri?: UriComponents; + /** + * Optional editor options to apply in the file + */ + readonly options?: IEditorOptions; + /** * An optional selection to apply in the file + * + * @deprecated Use options instead */ readonly selection?: { readonly startLineNumber: number; @@ -201,8 +209,12 @@ export interface IPathData { // if it exists readonly openOnlyIfExists?: boolean; - // Specifies an optional id to override the editor - // used to edit the resource, e.g. custom editor. + /** + * Specifies an optional id to override the editor + * used to edit the resource, e.g. custom editor. + * + * @deprecated Use options.override instead + */ readonly editorOverrideId?: string; } diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 1bf6c4750e2..8ab1929c10f 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -626,7 +626,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi endColumn: isNumber(file.selection.end.line) ? (isNumber(file.selection.end.column) ? file.selection.end.column : 1) : undefined, } : undefined, openOnlyIfExists: file.openOnlyIfExists, - editorOverrideId: file.openWith + editorOverrideId: file.openWith, + options: file.options }; }) }; diff --git a/src/vs/workbench/browser/web.api.ts b/src/vs/workbench/browser/web.api.ts index 9b365d6a9c5..9ddbf748e47 100644 --- a/src/vs/workbench/browser/web.api.ts +++ b/src/vs/workbench/browser/web.api.ts @@ -17,6 +17,7 @@ import type { TunnelProviderFeatures } from 'vs/platform/tunnel/common/tunnel'; import type { IProgress, IProgressCompositeOptions, IProgressDialogOptions, IProgressNotificationOptions, IProgressOptions, IProgressStep, IProgressWindowOptions } from 'vs/platform/progress/common/progress'; import { IObservableValue } from 'vs/base/common/observableValue'; import { TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; +import { IEditorOptions } from 'vs/platform/editor/common/editor'; /** * The `IWorkbench` interface is the API facade for web embedders @@ -580,9 +581,16 @@ export interface IRange { export interface IDefaultEditor { readonly uri: UriComponents; + /** + * @deprecated Use options instead + */ readonly selection?: IRange; readonly openOnlyIfExists?: boolean; + /** + * @deprecated Use options.override instead + */ readonly openWith?: string; + readonly options?: IEditorOptions; } export interface IDefaultLayout { diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index d94d70e015b..3c6225571ae 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -9,7 +9,7 @@ import { assertIsDefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditorViewState, IDiffEditor, IDiffEditorViewState, IEditorViewState } from 'vs/editor/common/editorCommon'; -import { IEditorOptions, ITextEditorOptions, IResourceEditorInput, ITextResourceEditorInput, IBaseTextResourceEditorInput, IBaseUntypedEditorInput } from 'vs/platform/editor/common/editor'; +import { IEditorOptions, IResourceEditorInput, ITextResourceEditorInput, IBaseTextResourceEditorInput, IBaseUntypedEditorInput } from 'vs/platform/editor/common/editor'; import type { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { IInstantiationService, IConstructorSignature, ServicesAccessor, BrandedService } from 'vs/platform/instantiation/common/instantiation'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -1340,8 +1340,11 @@ export async function pathsToEditors(paths: IPathData[] | undefined, fileService return; } - const options: ITextEditorOptions = { - selection: exists ? path.selection : undefined, + const options: IEditorOptions = { + ...path.options, + ...{ + selection: exists ? path.selection : undefined + }, pinned: true, override: path.editorOverrideId }; From 35c2141bf1babe9b8d18a86f80abcb6e6555b546 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Thu, 21 Apr 2022 15:02:11 -0700 Subject: [PATCH 210/245] only try to inject args when shell integration is enabled (#147872) --- .../terminal/node/terminalEnvironment.ts | 7 +- .../platform/terminal/node/terminalProcess.ts | 8 +-- .../test/node/terminalEnvironment.test.ts | 66 ++++++++++--------- 3 files changed, 42 insertions(+), 39 deletions(-) diff --git a/src/vs/platform/terminal/node/terminalEnvironment.ts b/src/vs/platform/terminal/node/terminalEnvironment.ts index 84c0f73c9a0..a679186010f 100644 --- a/src/vs/platform/terminal/node/terminalEnvironment.ts +++ b/src/vs/platform/terminal/node/terminalEnvironment.ts @@ -12,6 +12,7 @@ import * as process from 'vs/base/common/process'; import { format } from 'vs/base/common/strings'; import { isString } from 'vs/base/common/types'; import * as pfs from 'vs/base/node/pfs'; +import { ILogService } from 'vs/platform/log/common/log'; import { IShellLaunchConfig, ITerminalProcessOptions } from 'vs/platform/terminal/common/terminal'; export function getWindowsBuildNumber(): number { @@ -102,7 +103,8 @@ export interface IShellIntegrationConfigInjection { */ export function getShellIntegrationInjection( shellLaunchConfig: IShellLaunchConfig, - options: ITerminalProcessOptions['shellIntegration'] + options: ITerminalProcessOptions['shellIntegration'], + logService: ILogService ): IShellIntegrationConfigInjection | undefined { // Shell integration arg injection is disabled when: // - The global setting is disabled @@ -135,6 +137,7 @@ export function getShellIntegrationInjection( } return { newArgs }; } + logService.warn(`Shell integration cannot be enabled for executable "${shellLaunchConfig.executable}" and args`, shellLaunchConfig.args); return undefined; } @@ -207,7 +210,7 @@ export function getShellIntegrationInjection( return { newArgs, envMixin, filesToCopy }; } } - + logService.warn(`Shell integration cannot be enabled for executable "${shellLaunchConfig.executable}" and args`, shellLaunchConfig.args); return undefined; } diff --git a/src/vs/platform/terminal/node/terminalProcess.ts b/src/vs/platform/terminal/node/terminalProcess.ts index a7c1d444d4e..4b4be142397 100644 --- a/src/vs/platform/terminal/node/terminalProcess.ts +++ b/src/vs/platform/terminal/node/terminalProcess.ts @@ -198,11 +198,9 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess } let injection: IShellIntegrationConfigInjection | undefined; - if (this._options.shellIntegration) { - injection = getShellIntegrationInjection(this.shellLaunchConfig, this._options.shellIntegration); - if (!injection) { - this._logService.warn(`Shell integration cannot be enabled for executable "${this.shellLaunchConfig.executable}" and args`, this.shellLaunchConfig.args); - } else { + if (this._options.shellIntegration.enabled) { + injection = getShellIntegrationInjection(this.shellLaunchConfig, this._options.shellIntegration, this._logService); + if (injection) { if (injection.envMixin) { for (const [key, value] of Object.entries(injection.envMixin)) { this._ptyOptions.env ||= {}; diff --git a/src/vs/platform/terminal/test/node/terminalEnvironment.test.ts b/src/vs/platform/terminal/test/node/terminalEnvironment.test.ts index df8212d7c34..8dffe31a645 100644 --- a/src/vs/platform/terminal/test/node/terminalEnvironment.test.ts +++ b/src/vs/platform/terminal/test/node/terminalEnvironment.test.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { deepStrictEqual, ok, strictEqual } from 'assert'; +import { NullLogService } from 'vs/platform/log/common/log'; import { ITerminalProcessOptions } from 'vs/platform/terminal/common/terminal'; import { getShellIntegrationInjection, IShellIntegrationConfigInjection } from 'vs/platform/terminal/node/terminalEnvironment'; @@ -11,13 +12,14 @@ const enabledProcessOptions: ITerminalProcessOptions['shellIntegration'] = { ena const disabledProcessOptions: ITerminalProcessOptions['shellIntegration'] = { enabled: false, showWelcome: true }; const pwshExe = process.platform === 'win32' ? 'pwsh.exe' : 'pwsh'; const repoRoot = process.platform === 'win32' ? process.cwd()[0].toLowerCase() + process.cwd().substring(1) : process.cwd(); +const logService = new NullLogService(); suite('platform - terminalEnvironment', () => { suite('getShellIntegrationInjection', () => { suite('should not enable', () => { test('when isFeatureTerminal or when no executable is provided', () => { - ok(!getShellIntegrationInjection({ executable: pwshExe, args: ['-l', '-NoLogo'], isFeatureTerminal: true }, enabledProcessOptions)); - ok(getShellIntegrationInjection({ executable: pwshExe, args: ['-l', '-NoLogo'], isFeatureTerminal: false }, enabledProcessOptions)); + ok(!getShellIntegrationInjection({ executable: pwshExe, args: ['-l', '-NoLogo'], isFeatureTerminal: true }, enabledProcessOptions, logService)); + ok(getShellIntegrationInjection({ executable: pwshExe, args: ['-l', '-NoLogo'], isFeatureTerminal: false }, enabledProcessOptions, logService)); }); }); @@ -34,21 +36,21 @@ suite('platform - terminalEnvironment', () => { ] }); test('when undefined, []', () => { - deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: [] }, enabledProcessOptions), enabledExpectedResult); - deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: undefined }, enabledProcessOptions), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: [] }, enabledProcessOptions, logService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: undefined }, enabledProcessOptions, logService), enabledExpectedResult); }); suite('when no logo', () => { test('array - case insensitive', () => { - deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-NoLogo'] }, enabledProcessOptions), enabledExpectedResult); - deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-NOLOGO'] }, enabledProcessOptions), enabledExpectedResult); - deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-nol'] }, enabledProcessOptions), enabledExpectedResult); - deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-NOL'] }, enabledProcessOptions), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-NoLogo'] }, enabledProcessOptions, logService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-NOLOGO'] }, enabledProcessOptions, logService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-nol'] }, enabledProcessOptions, logService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-NOL'] }, enabledProcessOptions, logService), enabledExpectedResult); }); test('string - case insensitive', () => { - deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-NoLogo' }, enabledProcessOptions), enabledExpectedResult); - deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-NOLOGO' }, enabledProcessOptions), enabledExpectedResult); - deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-nol' }, enabledProcessOptions), enabledExpectedResult); - deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-NOL' }, enabledProcessOptions), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-NoLogo' }, enabledProcessOptions, logService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-NOLOGO' }, enabledProcessOptions, logService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-nol' }, enabledProcessOptions, logService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-NOL' }, enabledProcessOptions, logService), enabledExpectedResult); }); }); }); @@ -62,23 +64,23 @@ suite('platform - terminalEnvironment', () => { ] }); test('when array contains no logo and login', () => { - deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-l', '-NoLogo'] }, enabledProcessOptions), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-l', '-NoLogo'] }, enabledProcessOptions, logService), enabledExpectedResult); }); test('when string', () => { - deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-l' }, enabledProcessOptions), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-l' }, enabledProcessOptions, logService), enabledExpectedResult); }); }); suite('should not modify args', () => { test('when shell integration is disabled', () => { - strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-l'] }, disabledProcessOptions), undefined); - strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-l' }, disabledProcessOptions), undefined); - strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: undefined }, disabledProcessOptions), undefined); + strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-l'] }, disabledProcessOptions, logService), undefined); + strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-l' }, disabledProcessOptions, logService), undefined); + strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: undefined }, disabledProcessOptions, logService), undefined); }); test('when using unrecognized arg', () => { - strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-l', '-NoLogo', '-i'] }, disabledProcessOptions), undefined); + strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: ['-l', '-NoLogo', '-i'] }, disabledProcessOptions, logService), undefined); }); test('when using unrecognized arg (string)', () => { - strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-i' }, disabledProcessOptions), undefined); + strictEqual(getShellIntegrationInjection({ executable: pwshExe, args: '-i' }, disabledProcessOptions, logService), undefined); }); }); }); @@ -105,27 +107,27 @@ suite('platform - terminalEnvironment', () => { ok(result.filesToCopy[2].source.match(expectedSources[2])); } test('when undefined, []', () => { - const result1 = getShellIntegrationInjection({ executable: 'zsh', args: [] }, enabledProcessOptions); + const result1 = getShellIntegrationInjection({ executable: 'zsh', args: [] }, enabledProcessOptions, logService); deepStrictEqual(result1?.newArgs, ['-i']); assertIsEnabled(result1); - const result2 = getShellIntegrationInjection({ executable: 'zsh', args: undefined }, enabledProcessOptions); + const result2 = getShellIntegrationInjection({ executable: 'zsh', args: undefined }, enabledProcessOptions, logService); deepStrictEqual(result2?.newArgs, ['-i']); assertIsEnabled(result2); }); suite('should incorporate login arg', () => { test('when array', () => { - const result = getShellIntegrationInjection({ executable: 'zsh', args: ['-l'] }, enabledProcessOptions); + const result = getShellIntegrationInjection({ executable: 'zsh', args: ['-l'] }, enabledProcessOptions, logService); deepStrictEqual(result?.newArgs, ['-il']); assertIsEnabled(result); }); }); suite('should not modify args', () => { test('when shell integration is disabled', () => { - strictEqual(getShellIntegrationInjection({ executable: 'zsh', args: ['-l'] }, disabledProcessOptions), undefined); - strictEqual(getShellIntegrationInjection({ executable: 'zsh', args: undefined }, disabledProcessOptions), undefined); + strictEqual(getShellIntegrationInjection({ executable: 'zsh', args: ['-l'] }, disabledProcessOptions, logService), undefined); + strictEqual(getShellIntegrationInjection({ executable: 'zsh', args: undefined }, disabledProcessOptions, logService), undefined); }); test('when using unrecognized arg', () => { - strictEqual(getShellIntegrationInjection({ executable: 'zsh', args: ['-l', '-fake'] }, disabledProcessOptions), undefined); + strictEqual(getShellIntegrationInjection({ executable: 'zsh', args: ['-l', '-fake'] }, disabledProcessOptions, logService), undefined); }); }); }); @@ -140,9 +142,9 @@ suite('platform - terminalEnvironment', () => { ], envMixin: {} }); - deepStrictEqual(getShellIntegrationInjection({ executable: 'bash', args: [] }, enabledProcessOptions), enabledExpectedResult); - deepStrictEqual(getShellIntegrationInjection({ executable: 'bash', args: '' }, enabledProcessOptions), enabledExpectedResult); - deepStrictEqual(getShellIntegrationInjection({ executable: 'bash', args: undefined }, enabledProcessOptions), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: 'bash', args: [] }, enabledProcessOptions, logService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: 'bash', args: '' }, enabledProcessOptions, logService), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: 'bash', args: undefined }, enabledProcessOptions, logService), enabledExpectedResult); }); suite('should set login env variable and not modify args', () => { const enabledExpectedResult = Object.freeze({ @@ -155,16 +157,16 @@ suite('platform - terminalEnvironment', () => { } }); test('when array', () => { - deepStrictEqual(getShellIntegrationInjection({ executable: 'bash', args: ['-l'] }, enabledProcessOptions), enabledExpectedResult); + deepStrictEqual(getShellIntegrationInjection({ executable: 'bash', args: ['-l'] }, enabledProcessOptions, logService), enabledExpectedResult); }); }); suite('should not modify args', () => { test('when shell integration is disabled', () => { - strictEqual(getShellIntegrationInjection({ executable: 'bash', args: ['-l'] }, disabledProcessOptions), undefined); - strictEqual(getShellIntegrationInjection({ executable: 'bash', args: undefined }, disabledProcessOptions), undefined); + strictEqual(getShellIntegrationInjection({ executable: 'bash', args: ['-l'] }, disabledProcessOptions, logService), undefined); + strictEqual(getShellIntegrationInjection({ executable: 'bash', args: undefined }, disabledProcessOptions, logService), undefined); }); test('when custom array entry', () => { - strictEqual(getShellIntegrationInjection({ executable: 'bash', args: ['-l', '-i'] }, disabledProcessOptions), undefined); + strictEqual(getShellIntegrationInjection({ executable: 'bash', args: ['-l', '-i'] }, disabledProcessOptions, logService), undefined); }); }); }); From e4d0a1ff061ca8bc23c4459d6b38318354ba517b Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 21 Apr 2022 15:30:33 -0700 Subject: [PATCH 211/245] Support indexed base cell options. --- .../notebook/browser/notebookBrowser.ts | 3 ++- .../notebook/browser/notebookEditorWidget.ts | 22 +++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 34c9a2afe21..77a50b8c655 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -11,7 +11,7 @@ import { IEditorContributionDescription } from 'vs/editor/browser/editorExtensio import * as editorCommon from 'vs/editor/common/editorCommon'; import { FontInfo } from 'vs/editor/common/config/fontInfo'; import { IPosition } from 'vs/editor/common/core/position'; -import { Range } from 'vs/editor/common/core/range'; +import { IRange, Range } from 'vs/editor/common/core/range'; import { FindMatch, IModelDeltaDecoration, IReadonlyTextBuffer, ITextModel, TrackedRangeStickiness } from 'vs/editor/common/model'; import { MenuId } from 'vs/platform/actions/common/actions'; import { ITextEditorOptions, ITextResourceEditorInput } from 'vs/platform/editor/common/editor'; @@ -281,6 +281,7 @@ export interface INotebookEditorOptions extends ITextEditorOptions { readonly cellSelections?: ICellRange[]; readonly isReadOnly?: boolean; readonly viewState?: INotebookEditorViewState; + readonly indexedCellOptions?: { index: number; selection?: IRange }; } export type INotebookEditorContributionCtor = IConstructorSignature; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 40712d1eaa3..942f49772ca 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1220,8 +1220,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD this.viewModel.updateOptions({ isReadOnly: this._readOnly }); // reveal cell if editor options tell to do so - if (options?.cellOptions) { - const cellOptions = options.cellOptions; + const cellOptions = options?.cellOptions ?? this._parseIndexedCellOptions(options); + if (cellOptions) { const cell = this.viewModel.viewCells.find(cell => cell.uri.toString() === cellOptions.resource.toString()); if (cell) { this.focusElement(cell); @@ -1274,6 +1274,24 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD this._onDidChangeOptions.fire(); } + private _parseIndexedCellOptions(options: INotebookEditorOptions | undefined) { + if (options?.indexedCellOptions) { + // convert index based selections + const cell = this.cellAt(options.indexedCellOptions.index); + if (cell) { + return { + resource: cell.uri, + options: { + selection: options.indexedCellOptions.selection, + preserveFocus: false + } + }; + } + } + + return undefined; + } + private _detachModel() { this._localStore.clear(); dispose(this._localCellStateListeners); From 1e55117c0247a11952a2dca4ba10176831b09f1c Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 21 Apr 2022 09:56:20 -0700 Subject: [PATCH 212/245] Fix bad regex #145265 --- src/vs/workbench/contrib/debug/node/terminals.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/node/terminals.ts b/src/vs/workbench/contrib/debug/node/terminals.ts index d4338a5c04c..5418d9e8ffc 100644 --- a/src/vs/workbench/contrib/debug/node/terminals.ts +++ b/src/vs/workbench/contrib/debug/node/terminals.ts @@ -139,7 +139,7 @@ export function prepareCommand(shell: string, args: string[], cwd?: string, env? if (value === null) { command += `set "${key}=" && `; } else { - value = value.replace(/[^&|<>]/g, s => `^${s}`); + value = value.replace(/[&^|<>]/g, s => `^${s}`); command += `set "${key}=${value}" && `; } } From 9425cdded7fd229fd83df46774620f53e3106acc Mon Sep 17 00:00:00 2001 From: Max Belsky Date: Fri, 22 Apr 2022 01:06:23 +0200 Subject: [PATCH 213/245] Disable a breakpoint on shift+click (#147598) * Disable a breakpoint on shift+click * Disable a condition breakpoint/logpoint on shift+click * Update variable names * Apply suggestions from review * Fix shift+click for enabling conditional breakpoints * Remove duplicated check Co-authored-by: Rob Lourens --- .../browser/breakpointEditorContribution.ts | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts index 7d8351ddb7d..4eab6791a7f 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts @@ -235,38 +235,48 @@ export class BreakpointEditorContribution implements IBreakpointEditorContributi const breakpoints = this.debugService.getModel().getBreakpoints({ uri, lineNumber }); if (breakpoints.length) { - // Show the dialog if there is a potential condition to be accidently lost. - // Do not show dialog on linux due to electron issue freezing the mouse #50026 - if (!env.isLinux && breakpoints.some(bp => !!bp.condition || !!bp.logMessage || !!bp.hitCondition)) { + const isShiftPressed = e.event.shiftKey; + const enabled = breakpoints.some(bp => bp.enabled); + + if (isShiftPressed) { + breakpoints.forEach(bp => this.debugService.enableOrDisableBreakpoints(!enabled, bp)); + } else if (!env.isLinux && breakpoints.some(bp => !!bp.condition || !!bp.logMessage || !!bp.hitCondition)) { + // Show the dialog if there is a potential condition to be accidently lost. + // Do not show dialog on linux due to electron issue freezing the mouse #50026 const logPoint = breakpoints.every(bp => !!bp.logMessage); const breakpointType = logPoint ? nls.localize('logPoint', "Logpoint") : nls.localize('breakpoint', "Breakpoint"); - const disable = breakpoints.some(bp => bp.enabled); - const enabling = nls.localize('breakpointHasConditionDisabled', + const disabledBreakpointDialogMessage = nls.localize( + 'breakpointHasConditionDisabled', "This {0} has a {1} that will get lost on remove. Consider enabling the {0} instead.", breakpointType.toLowerCase(), logPoint ? nls.localize('message', "message") : nls.localize('condition', "condition") ); - const disabling = nls.localize('breakpointHasConditionEnabled', + const enabledBreakpointDialogMessage = nls.localize( + 'breakpointHasConditionEnabled', "This {0} has a {1} that will get lost on remove. Consider disabling the {0} instead.", breakpointType.toLowerCase(), logPoint ? nls.localize('message', "message") : nls.localize('condition', "condition") ); - const { choice } = await this.dialogService.show(severity.Info, disable ? disabling : enabling, [ - nls.localize('removeLogPoint', "Remove {0}", breakpointType), - nls.localize('disableLogPoint', "{0} {1}", disable ? nls.localize('disable', "Disable") : nls.localize('enable', "Enable"), breakpointType), - nls.localize('cancel', "Cancel") - ], { cancelId: 2 }); + const { choice } = await this.dialogService.show( + severity.Info, + enabled ? enabledBreakpointDialogMessage : disabledBreakpointDialogMessage, + [ + nls.localize('removeLogPoint', "Remove {0}", breakpointType), + nls.localize('disableLogPoint', "{0} {1}", enabled ? nls.localize('disable', "Disable") : nls.localize('enable', "Enable"), breakpointType), + nls.localize('cancel', "Cancel") + ], + { cancelId: 2 }, + ); if (choice === 0) { breakpoints.forEach(bp => this.debugService.removeBreakpoints(bp.getId())); } if (choice === 1) { - breakpoints.forEach(bp => this.debugService.enableOrDisableBreakpoints(!disable, bp)); + breakpoints.forEach(bp => this.debugService.enableOrDisableBreakpoints(!enabled, bp)); } } else { - const enabled = breakpoints.some(bp => bp.enabled); if (!enabled) { breakpoints.forEach(bp => this.debugService.enableOrDisableBreakpoints(!enabled, bp)); } else { From 3c8d308a1f1f5a80c605912ba761ea0cbfac866c Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Thu, 21 Apr 2022 16:59:35 -0700 Subject: [PATCH 214/245] add max threshold for find results for which to show decorations (#147879) --- package.json | 8 ++--- remote/package.json | 8 ++--- remote/web/package.json | 6 ++-- remote/web/yarn.lock | 24 +++++++------- remote/yarn.lock | 32 +++++++++---------- .../browser/find/simpleFindWidget.ts | 10 ++++-- yarn.lock | 32 +++++++++---------- 7 files changed, 63 insertions(+), 57 deletions(-) diff --git a/package.json b/package.json index 16fa1bcdd1b..e064b6f1cd8 100644 --- a/package.json +++ b/package.json @@ -84,12 +84,12 @@ "vscode-proxy-agent": "^0.12.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.24", - "xterm-addon-search": "0.9.0-beta.21", + "xterm": "4.19.0-beta.25", + "xterm-addon-search": "0.9.0-beta.22", "xterm-addon-serialize": "0.7.0-beta.12", "xterm-addon-unicode11": "0.4.0-beta.3", - "xterm-addon-webgl": "0.12.0-beta.28", - "xterm-headless": "4.19.0-beta.24", + "xterm-addon-webgl": "0.12.0-beta.29", + "xterm-headless": "4.19.0-beta.25", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/package.json b/remote/package.json index 0424596dfd3..8be2005cde1 100644 --- a/remote/package.json +++ b/remote/package.json @@ -24,12 +24,12 @@ "vscode-proxy-agent": "^0.12.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.24", - "xterm-addon-search": "0.9.0-beta.21", + "xterm": "4.19.0-beta.25", + "xterm-addon-search": "0.9.0-beta.22", "xterm-addon-serialize": "0.7.0-beta.12", "xterm-addon-unicode11": "0.4.0-beta.3", - "xterm-addon-webgl": "0.12.0-beta.28", - "xterm-headless": "4.19.0-beta.24", + "xterm-addon-webgl": "0.12.0-beta.29", + "xterm-headless": "4.19.0-beta.25", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/web/package.json b/remote/web/package.json index b2543988547..d3dad3b383a 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -10,9 +10,9 @@ "tas-client-umd": "0.1.4", "vscode-oniguruma": "1.6.1", "vscode-textmate": "7.0.1", - "xterm": "4.19.0-beta.24", - "xterm-addon-search": "0.9.0-beta.21", + "xterm": "4.19.0-beta.25", + "xterm-addon-search": "0.9.0-beta.22", "xterm-addon-unicode11": "0.4.0-beta.3", - "xterm-addon-webgl": "0.12.0-beta.28" + "xterm-addon-webgl": "0.12.0-beta.29" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 2094e08a208..e15a4cf241a 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -113,22 +113,22 @@ vscode-textmate@7.0.1: resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-7.0.1.tgz#8118a32b02735dccd14f893b495fa5389ad7de79" integrity sha512-zQ5U/nuXAAMsh691FtV0wPz89nSkHbs+IQV8FDk+wew9BlSDhf4UmWGlWJfTR2Ti6xZv87Tj5fENzKf6Qk7aLw== -xterm-addon-search@0.9.0-beta.21: - version "0.9.0-beta.21" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.21.tgz#5348fe25676cdd89ce3be52ae62a316b6f266176" - integrity sha512-jh6kfRCpWRvZZkV9QghFYesSYHjybaLNEyYAD6uilZYfNHoGLa0zPgUkLOqoECL7K6rhBmSYOkKbc9MG4wNFMQ== +xterm-addon-search@0.9.0-beta.22: + version "0.9.0-beta.22" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.22.tgz#18f2eabda82709cdd64dee003879b586869e28a3" + integrity sha512-1I8W0bwjg6bUoNaESKHSkS9BN3Z9Xe44/zbUb6Un+pbb5Nun4LQvFwSLkAIdRrslxcbcHYIU/2Q7F1wCOWLbaA== 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.28: - version "0.12.0-beta.28" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.28.tgz#252f88fd15816c23789659a0e2545682cf1eec9c" - integrity sha512-xpqZRYlyv+aNdDl46W2Hi2fxakNvdJDmWhOwGHPjOmex+kOYdBvVn4rRZmJ7xrKFuQVOfzb3SQCmpZ/njCpBJA== +xterm-addon-webgl@0.12.0-beta.29: + version "0.12.0-beta.29" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.29.tgz#7a508595c4521d14d7ed4315a121f9e3f230a0f0" + integrity sha512-NcZBsD0ar3ZpQX070hDIsyEBl/StRMNu6U+9crNpiD2rQVfkM1vcWkOv31Zlj3eu6/f8z5aStyZLRMCGFwiRbA== -xterm@4.19.0-beta.24: - version "4.19.0-beta.24" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.24.tgz#12b62877d4cf133f4626db3a15f6ed267b3bbcb5" - integrity sha512-GydvC2ExdsfjkiXhUVuZtDhc89IZfyMLFgw/nwWHrvkyq4iCVsHbMloEObV3lZmv+0oRpcGtTiorfzdWGiHG0A== +xterm@4.19.0-beta.25: + version "4.19.0-beta.25" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.25.tgz#38f92d0fef1cfdb290ef8994449a04fa1a8c90a7" + integrity sha512-pDiMWKN1Cj4+X/K9Xegp0SA0ZDEGVqiq7RPSy8oZO2wo2rze1BF20PAZb3/RSp30eY5WyOKilKnck4yNOsPzHw== diff --git a/remote/yarn.lock b/remote/yarn.lock index 3bd6b395860..ac114377345 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -914,10 +914,10 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -xterm-addon-search@0.9.0-beta.21: - version "0.9.0-beta.21" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.21.tgz#5348fe25676cdd89ce3be52ae62a316b6f266176" - integrity sha512-jh6kfRCpWRvZZkV9QghFYesSYHjybaLNEyYAD6uilZYfNHoGLa0zPgUkLOqoECL7K6rhBmSYOkKbc9MG4wNFMQ== +xterm-addon-search@0.9.0-beta.22: + version "0.9.0-beta.22" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.22.tgz#18f2eabda82709cdd64dee003879b586869e28a3" + integrity sha512-1I8W0bwjg6bUoNaESKHSkS9BN3Z9Xe44/zbUb6Un+pbb5Nun4LQvFwSLkAIdRrslxcbcHYIU/2Q7F1wCOWLbaA== xterm-addon-serialize@0.7.0-beta.12: version "0.7.0-beta.12" @@ -929,20 +929,20 @@ 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.28: - version "0.12.0-beta.28" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.28.tgz#252f88fd15816c23789659a0e2545682cf1eec9c" - integrity sha512-xpqZRYlyv+aNdDl46W2Hi2fxakNvdJDmWhOwGHPjOmex+kOYdBvVn4rRZmJ7xrKFuQVOfzb3SQCmpZ/njCpBJA== +xterm-addon-webgl@0.12.0-beta.29: + version "0.12.0-beta.29" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.29.tgz#7a508595c4521d14d7ed4315a121f9e3f230a0f0" + integrity sha512-NcZBsD0ar3ZpQX070hDIsyEBl/StRMNu6U+9crNpiD2rQVfkM1vcWkOv31Zlj3eu6/f8z5aStyZLRMCGFwiRbA== -xterm-headless@4.19.0-beta.24: - version "4.19.0-beta.24" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.24.tgz#17dab68ae1b2600b0926d4c47032599f3b2f24cc" - integrity sha512-Coq8gQ9f5/kNr1yhpXTZqatTOHOYDF4XVp93SM12vRROH64bM0rUMec6ukuTAd8DD97UOWYUgR+n7+xD0iTTSg== +xterm-headless@4.19.0-beta.25: + version "4.19.0-beta.25" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.25.tgz#a0a1b59f386c44458f06b8ced64e3567371cc983" + integrity sha512-UswSgymk3g9i6XTpFAasnqqIvWhi+AEWT+iO3kkjII6ll+dYEQgeZAv92EnCmeRHp11u5TP+IBAo8jy+aTYbtA== -xterm@4.19.0-beta.24: - version "4.19.0-beta.24" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.24.tgz#12b62877d4cf133f4626db3a15f6ed267b3bbcb5" - integrity sha512-GydvC2ExdsfjkiXhUVuZtDhc89IZfyMLFgw/nwWHrvkyq4iCVsHbMloEObV3lZmv+0oRpcGtTiorfzdWGiHG0A== +xterm@4.19.0-beta.25: + version "4.19.0-beta.25" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.25.tgz#38f92d0fef1cfdb290ef8994449a04fa1a8c90a7" + integrity sha512-pDiMWKN1Cj4+X/K9Xegp0SA0ZDEGVqiq7RPSy8oZO2wo2rze1BF20PAZb3/RSp30eY5WyOKilKnck4yNOsPzHw== yallist@^4.0.0: version "4.0.0" diff --git a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts index 1e220e2de14..f49139355d7 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget.ts @@ -12,13 +12,14 @@ import { Delayer } from 'vs/base/common/async'; import { KeyCode } from 'vs/base/common/keyCodes'; import { FindReplaceState } from 'vs/editor/contrib/find/browser/findState'; import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox'; -import { SimpleButton, findPreviousMatchIcon, findNextMatchIcon } from 'vs/editor/contrib/find/browser/findWidget'; +import { SimpleButton, findPreviousMatchIcon, findNextMatchIcon, NLS_NO_RESULTS, NLS_MATCHES_LOCATION } from 'vs/editor/contrib/find/browser/findWidget'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { editorWidgetBackground, inputActiveOptionBorder, inputActiveOptionBackground, inputActiveOptionForeground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground, errorForeground } from 'vs/platform/theme/common/colorRegistry'; import { IColorTheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ContextScopedFindInput } from 'vs/platform/history/browser/contextScopedHistoryWidget'; import { widgetClose } from 'vs/platform/theme/common/iconRegistry'; +import * as strings from 'vs/base/common/strings'; const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find"); const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find"); @@ -307,7 +308,12 @@ export abstract class SimpleFindWidget extends Widget { this._matchesCount.className = 'matchesCount'; } this._matchesCount.innerText = ''; - const label = count === undefined || count.resultCount === 0 ? `No Results` : `${count.resultIndex + 1} of ${count.resultCount}`; + let label; + if (count?.resultCount === -1) { + label = ''; + } else { + label = count === undefined || count.resultCount === 0 ? NLS_NO_RESULTS : strings.format(NLS_MATCHES_LOCATION, count.resultIndex + 1, count?.resultCount); + } this._matchesCount.appendChild(document.createTextNode(label)); this._matchesCount.classList.toggle('no-results', !count || count.resultCount === 0); this._findInput?.domNode.insertAdjacentElement('afterend', this._matchesCount); diff --git a/yarn.lock b/yarn.lock index 0e1a8164c4d..63afc242b61 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12204,10 +12204,10 @@ xtend@~2.1.1: dependencies: object-keys "~0.4.0" -xterm-addon-search@0.9.0-beta.21: - version "0.9.0-beta.21" - resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.21.tgz#5348fe25676cdd89ce3be52ae62a316b6f266176" - integrity sha512-jh6kfRCpWRvZZkV9QghFYesSYHjybaLNEyYAD6uilZYfNHoGLa0zPgUkLOqoECL7K6rhBmSYOkKbc9MG4wNFMQ== +xterm-addon-search@0.9.0-beta.22: + version "0.9.0-beta.22" + resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.22.tgz#18f2eabda82709cdd64dee003879b586869e28a3" + integrity sha512-1I8W0bwjg6bUoNaESKHSkS9BN3Z9Xe44/zbUb6Un+pbb5Nun4LQvFwSLkAIdRrslxcbcHYIU/2Q7F1wCOWLbaA== xterm-addon-serialize@0.7.0-beta.12: version "0.7.0-beta.12" @@ -12219,20 +12219,20 @@ 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.28: - version "0.12.0-beta.28" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.28.tgz#252f88fd15816c23789659a0e2545682cf1eec9c" - integrity sha512-xpqZRYlyv+aNdDl46W2Hi2fxakNvdJDmWhOwGHPjOmex+kOYdBvVn4rRZmJ7xrKFuQVOfzb3SQCmpZ/njCpBJA== +xterm-addon-webgl@0.12.0-beta.29: + version "0.12.0-beta.29" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.29.tgz#7a508595c4521d14d7ed4315a121f9e3f230a0f0" + integrity sha512-NcZBsD0ar3ZpQX070hDIsyEBl/StRMNu6U+9crNpiD2rQVfkM1vcWkOv31Zlj3eu6/f8z5aStyZLRMCGFwiRbA== -xterm-headless@4.19.0-beta.24: - version "4.19.0-beta.24" - resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.24.tgz#17dab68ae1b2600b0926d4c47032599f3b2f24cc" - integrity sha512-Coq8gQ9f5/kNr1yhpXTZqatTOHOYDF4XVp93SM12vRROH64bM0rUMec6ukuTAd8DD97UOWYUgR+n7+xD0iTTSg== +xterm-headless@4.19.0-beta.25: + version "4.19.0-beta.25" + resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.19.0-beta.25.tgz#a0a1b59f386c44458f06b8ced64e3567371cc983" + integrity sha512-UswSgymk3g9i6XTpFAasnqqIvWhi+AEWT+iO3kkjII6ll+dYEQgeZAv92EnCmeRHp11u5TP+IBAo8jy+aTYbtA== -xterm@4.19.0-beta.24: - version "4.19.0-beta.24" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.24.tgz#12b62877d4cf133f4626db3a15f6ed267b3bbcb5" - integrity sha512-GydvC2ExdsfjkiXhUVuZtDhc89IZfyMLFgw/nwWHrvkyq4iCVsHbMloEObV3lZmv+0oRpcGtTiorfzdWGiHG0A== +xterm@4.19.0-beta.25: + version "4.19.0-beta.25" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.19.0-beta.25.tgz#38f92d0fef1cfdb290ef8994449a04fa1a8c90a7" + integrity sha512-pDiMWKN1Cj4+X/K9Xegp0SA0ZDEGVqiq7RPSy8oZO2wo2rze1BF20PAZb3/RSp30eY5WyOKilKnck4yNOsPzHw== y18n@^3.2.1: version "3.2.2" From 1d2461bed6b33fd5439792595f33453def5a92d0 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 19 Apr 2022 20:16:14 -0700 Subject: [PATCH 215/245] Sort --- extensions/typescript-language-features/src/languageProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index fa2ca6563b4..321a2c845b3 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -73,6 +73,7 @@ export default class LanguageProvider extends Disposable { import('./languageFeatures/formatting').then(provider => this._register(provider.register(selector, this.description, this.client, this.fileConfigurationManager))), import('./languageFeatures/hover').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager))), import('./languageFeatures/implementations').then(provider => this._register(provider.register(selector, this.client))), + import('./languageFeatures/inlayHints').then(provider => this._register(provider.register(selector, this.description, this.client, this.fileConfigurationManager))), import('./languageFeatures/jsDocCompletions').then(provider => this._register(provider.register(selector, this.description, this.client, this.fileConfigurationManager))), import('./languageFeatures/organizeImports').then(provider => this._register(provider.register(selector, this.client, this.commandManager, this.fileConfigurationManager, this.telemetryReporter))), import('./languageFeatures/quickFix').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.client.diagnosticsManager, this.telemetryReporter))), @@ -85,7 +86,6 @@ export default class LanguageProvider extends Disposable { import('./languageFeatures/sourceDefinition').then(provider => this._register(provider.register(this.client, this.commandManager))), import('./languageFeatures/tagClosing').then(provider => this._register(provider.register(selector, this.description, this.client))), import('./languageFeatures/typeDefinitions').then(provider => this._register(provider.register(selector, this.client))), - import('./languageFeatures/inlayHints').then(provider => this._register(provider.register(selector, this.description, this.client, this.fileConfigurationManager))), ]); } From 9fbd9629731e0c2df1a9e02dec368d8ea2d45309 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 20 Apr 2022 10:56:26 -0700 Subject: [PATCH 216/245] Differentiate md refs request on file path vs on fragment --- .../src/languageFeatures/references.ts | 24 ++++++-- .../src/test/references.test.ts | 55 ++++++++++++++++++- 2 files changed, 71 insertions(+), 8 deletions(-) diff --git a/extensions/markdown-language-features/src/languageFeatures/references.ts b/extensions/markdown-language-features/src/languageFeatures/references.ts index 2cc23912445..158da7204a7 100644 --- a/extensions/markdown-language-features/src/languageFeatures/references.ts +++ b/extensions/markdown-language-features/src/languageFeatures/references.ts @@ -136,11 +136,11 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference if (link.ref.range.contains(position)) { return Array.from(this.getReferencesToLinkReference(docLinks, link.ref.text, { resource: document.uri, range: link.ref.range })); } else if (link.source.hrefRange.contains(position)) { - return this.getReferencesToLink(link, token); + return this.getReferencesToLink(link, position, token); } } else { if (link.source.hrefRange.contains(position)) { - return this.getReferencesToLink(link, token); + return this.getReferencesToLink(link, position, token); } } } @@ -148,7 +148,7 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference return []; } - private async getReferencesToLink(sourceLink: MdLink, token: vscode.CancellationToken): Promise { + private async getReferencesToLink(sourceLink: MdLink, triggerPosition: vscode.Position, token: vscode.CancellationToken): Promise { const allLinksInWorkspace = (await this._linkCache.getAll()).flat(); if (token.isCancellationRequested) { return []; @@ -191,7 +191,7 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference const references: MdReference[] = []; - if (sourceLink.href.fragment) { + if (sourceLink.href.fragment && sourceLink.source.fragmentRange?.contains(triggerPosition)) { const toc = await TableOfContents.create(this.engine, targetDoc); const entry = toc.lookup(sourceLink.href.fragment); if (entry) { @@ -250,12 +250,13 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference } const isTriggerLocation = !!sourceLink && sourceLink.source.resource.fsPath === link.source.resource.fsPath && sourceLink.source.hrefRange.isEqual(link.source.hrefRange); + const pathRange = this.getPathRange(link); yield { kind: 'link', isTriggerLocation, isDefinition: false, link, - location: new vscode.Location(link.source.resource, link.source.hrefRange), + location: new vscode.Location(link.source.resource, pathRange), }; } } @@ -274,14 +275,25 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference if (ref === refToFind && link.source.resource.fsPath === from.resource.fsPath) { const isTriggerLocation = from.resource.fsPath === link.source.resource.fsPath && ( (link.href.kind === 'reference' && from.range.isEqual(link.source.hrefRange)) || (link.kind === 'definition' && from.range.isEqual(link.ref.range))); + + const pathRange = this.getPathRange(link); yield { kind: 'link', isTriggerLocation, isDefinition: link.kind === 'definition', link, - location: new vscode.Location(from.resource, link.source.hrefRange), + location: new vscode.Location(from.resource, pathRange), }; } } } + + /** + * Get just the range of the file path, dropping the fragment + */ + private getPathRange(link: MdLink): vscode.Range { + return link.source.fragmentRange + ? link.source.hrefRange.with(undefined, link.source.fragmentRange.start.translate(0, -1)) + : link.source.hrefRange; + } } diff --git a/extensions/markdown-language-features/src/test/references.test.ts b/extensions/markdown-language-features/src/test/references.test.ts index 1a1c5f444b2..45925ffddf9 100644 --- a/extensions/markdown-language-features/src/test/references.test.ts +++ b/extensions/markdown-language-features/src/test/references.test.ts @@ -23,7 +23,7 @@ function getReferences(doc: InMemoryDocument, pos: vscode.Position, workspaceCon return provider.provideReferences(doc, pos, { includeDeclaration: true }, noopToken); } -function assertReferencesEqual(actualRefs: readonly vscode.Location[], ...expectedRefs: { uri: vscode.Uri; line: number }[]) { +function assertReferencesEqual(actualRefs: readonly vscode.Location[], ...expectedRefs: { uri: vscode.Uri; line: number; startCharacter?: number; endCharacter?: number }[]) { assert.strictEqual(actualRefs.length, expectedRefs.length, `Reference counts should match`); for (let i = 0; i < actualRefs.length; ++i) { @@ -32,6 +32,12 @@ function assertReferencesEqual(actualRefs: readonly vscode.Location[], ...expect assert.strictEqual(actual.uri.toString(), expected.uri.toString(), `Ref '${i}' has expected document`); assert.strictEqual(actual.range.start.line, expected.line, `Ref '${i}' has expected start line`); assert.strictEqual(actual.range.end.line, expected.line, `Ref '${i}' has expected end line`); + if (typeof expected.startCharacter !== 'undefined') { + assert.strictEqual(actual.range.start.character, expected.startCharacter, `Ref '${i}' has expected start character`); + } + if (typeof expected.endCharacter !== 'undefined') { + assert.strictEqual(actual.range.end.character, expected.endCharacter, `Ref '${i}' has expected end character`); + } } } @@ -265,7 +271,7 @@ suite('markdown: find all references', () => { `[without ext](./sub/other.md#header)`, )); - const refs = await getReferences(doc, new vscode.Position(0, 15), new InMemoryWorkspaceMarkdownDocuments([ + const refs = await getReferences(doc, new vscode.Position(0, 23), new InMemoryWorkspaceMarkdownDocuments([ doc, new InMemoryDocument(other1Uri, joinLines( `pre`, @@ -415,6 +421,51 @@ suite('markdown: find all references', () => { ); }); + test('Should distinguish between references to file and to header within file', async () => { + const docUri = workspacePath('doc.md'); + const other1Uri = workspacePath('sub', 'other.md'); + + const doc = new InMemoryDocument(docUri, joinLines( + `# abc`, + ``, + `[link 1](#abc)`, + )); + const otherDoc = new InMemoryDocument(other1Uri, joinLines( + `[link](/doc.md#abc)`, + `[link no text](/doc#abc)`, + )); + const workspaceContents = new InMemoryWorkspaceMarkdownDocuments([ + doc, + otherDoc, + ]); + { + // Check refs to header fragment + const headerRefs = await getReferences(otherDoc, new vscode.Position(0, 16), workspaceContents); + assertReferencesEqual(headerRefs!, + { uri: docUri, line: 0 }, // Header definition + { uri: docUri, line: 2 }, + { uri: other1Uri, line: 0 }, + { uri: other1Uri, line: 1 }, + ); + } + { + // Check refs to file itself from link with ext + const fileRefs = await getReferences(otherDoc, new vscode.Position(0, 9), workspaceContents); + assertReferencesEqual(fileRefs!, + { uri: other1Uri, line: 0, endCharacter: 14 }, + { uri: other1Uri, line: 1, endCharacter: 19 }, + ); + } + { + // Check refs to file itself from link without ext + const fileRefs = await getReferences(otherDoc, new vscode.Position(1, 17), workspaceContents); + assertReferencesEqual(fileRefs!, + { uri: other1Uri, line: 0 }, + { uri: other1Uri, line: 1 }, + ); + } + }); + suite('Reference links', () => { test('Should find reference links within file from link', async () => { const docUri = workspacePath('doc.md'); From 0ac39e800d3d4de7b9612118559ece9af9cda5cb Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 20 Apr 2022 16:38:30 -0700 Subject: [PATCH 217/245] Add support for renaming files in markdown using F2 This lets you trigger F2 on a file path in a markdown link to both rename the file and also update all references to it --- .../src/extension.ts | 2 +- .../src/languageFeatures/references.ts | 32 ++-- .../src/languageFeatures/rename.ts | 129 +++++++++++-- .../src/test/references.test.ts | 24 ++- .../src/test/rename.test.ts | 180 +++++++++++++++--- 5 files changed, 304 insertions(+), 63 deletions(-) diff --git a/extensions/markdown-language-features/src/extension.ts b/extensions/markdown-language-features/src/extension.ts index e0c0477cce4..c75ba435252 100644 --- a/extensions/markdown-language-features/src/extension.ts +++ b/extensions/markdown-language-features/src/extension.ts @@ -71,7 +71,7 @@ function registerMarkdownLanguageFeatures( 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, githubSlugifier)), + vscode.languages.registerRenameProvider(selector, new MdRenameProvider(referencesProvider, workspaceContents, githubSlugifier)), MdPathCompletionProvider.register(selector, engine, linkProvider), registerDropIntoEditor(selector), registerFindFileReferences(commandManager, referencesProvider), diff --git a/extensions/markdown-language-features/src/languageFeatures/references.ts b/extensions/markdown-language-features/src/languageFeatures/references.ts index 158da7204a7..b60bb447c7f 100644 --- a/extensions/markdown-language-features/src/languageFeatures/references.ts +++ b/extensions/markdown-language-features/src/languageFeatures/references.ts @@ -176,22 +176,14 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference return references; } - let targetDoc = await this.workspaceContents.getMarkdownDocument(sourceLink.href.path); - if (!targetDoc) { - // 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(sourceLink.href.path) === '') { - const dotMdResource = sourceLink.href.path.with({ path: sourceLink.href.path.path + '.md' }); - targetDoc = await this.workspaceContents.getMarkdownDocument(dotMdResource); - } - } - - if (!targetDoc || token.isCancellationRequested) { + const targetDoc = await tryFindMdDocumentForLink(sourceLink.href, this.workspaceContents); + if (token.isCancellationRequested) { return []; } const references: MdReference[] = []; - if (sourceLink.href.fragment && sourceLink.source.fragmentRange?.contains(triggerPosition)) { + if (targetDoc && sourceLink.href.fragment && sourceLink.source.fragmentRange?.contains(triggerPosition)) { const toc = await TableOfContents.create(this.engine, targetDoc); const entry = toc.lookup(sourceLink.href.fragment); if (entry) { @@ -222,7 +214,7 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference } } } else { // Triggered on a link without a fragment so we only require matching the file and ignore fragments - references.push(...this.findAllLinksToFile(targetDoc.uri, allLinksInWorkspace, sourceLink)); + references.push(...this.findAllLinksToFile(targetDoc?.uri ?? sourceLink.href.path, allLinksInWorkspace, sourceLink)); } return references; @@ -297,3 +289,19 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference : link.source.hrefRange; } } + +export async function tryFindMdDocumentForLink(href: InternalHref, workspaceContents: MdWorkspaceContents): Promise { + const targetDoc = await workspaceContents.getMarkdownDocument(href.path); + if (targetDoc) { + return targetDoc; + } + + // 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); + } + + return undefined; +} + diff --git a/extensions/markdown-language-features/src/languageFeatures/rename.ts b/extensions/markdown-language-features/src/languageFeatures/rename.ts index 51379366730..9bad2489ab9 100644 --- a/extensions/markdown-language-features/src/languageFeatures/rename.ts +++ b/extensions/markdown-language-features/src/languageFeatures/rename.ts @@ -6,12 +6,35 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import { Slugifier } from '../slugify'; import { Disposable } from '../util/dispose'; -import { SkinnyTextDocument } from '../workspaceContents'; -import { MdHeaderReference, MdReference, MdReferencesProvider } from './references'; +import { resolveDocumentLink } from '../util/openDocumentLink'; +import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; +import { InternalHref } from './documentLinkProvider'; +import { MdHeaderReference, MdLinkReference, MdReference, MdReferencesProvider, tryFindMdDocumentForLink } from './references'; const localize = nls.loadMessageBundle(); +export interface MdReferencesResponse { + references: MdReference[]; + triggerRef: MdReference; +} + +interface MdFileRenameEdit { + readonly from: string; + readonly to: string; +} + +/** + * Type with additional metadata about the edits for testing + * + * This is needed since `vscode.WorkspaceEdit` does not expose info on file renames. + */ +export interface MdWorkspaceEdit { + readonly edit: vscode.WorkspaceEdit; + + readonly fileRenames?: ReadonlyArray; +} + export class MdRenameProvider extends Disposable implements vscode.RenameProvider { private cachedRefs?: { @@ -22,8 +45,11 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide readonly references: MdReference[]; } | undefined; + private readonly renameNotSupportedText = localize('invalidRenameLocation', "Rename not supported at location"); + public constructor( private readonly referencesProvider: MdReferencesProvider, + private readonly workspaceContents: MdWorkspaceContents, private readonly slugifier: Slugifier, ) { super(); @@ -36,7 +62,7 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide } if (!allRefsInfo || !allRefsInfo.references.length) { - throw new Error(localize('invalidRenameLocation', "Rename not supported at location")); + throw new Error(this.renameNotSupportedText); } const triggerRef = allRefsInfo.triggerRef; @@ -56,8 +82,9 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide return { range: triggerRef.link.source.hrefRange, placeholder: document.getText(triggerRef.link.source.hrefRange) }; } + // See if we are renaming the fragment or the path const { fragmentRange } = triggerRef.link.source; - if (fragmentRange) { + if (fragmentRange?.contains(position)) { const declaration = this.findHeaderDeclaration(allRefsInfo.references); if (declaration) { return { range: fragmentRange, placeholder: declaration.headerText }; @@ -65,16 +92,31 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide return { range: fragmentRange, placeholder: document.getText(fragmentRange) }; } - throw new Error(localize('renameNoFiles', "Renaming files is currently not supported")); + const range = this.getFilePathRange(triggerRef); + if (!range) { + throw new Error(this.renameNotSupportedText); + } + return { range, placeholder: document.getText(range) }; } } } + private getFilePathRange(ref: MdLinkReference): vscode.Range { + if (ref.link.source.fragmentRange) { + return ref.link.source.hrefRange.with(undefined, ref.link.source.fragmentRange.start.translate(0, -1)); + } + return ref.link.source.hrefRange; + } + private findHeaderDeclaration(references: readonly MdReference[]): MdHeaderReference | undefined { return references.find(ref => ref.isDefinition && ref.kind === 'header') as MdHeaderReference | undefined; } public async provideRenameEdits(document: SkinnyTextDocument, position: vscode.Position, newName: string, token: vscode.CancellationToken): Promise { + return (await this.provideRenameEditsImpl(document, position, newName, token))?.edit; + } + + public async provideRenameEditsImpl(document: SkinnyTextDocument, position: vscode.Position, newName: string, token: vscode.CancellationToken): Promise { const allRefsInfo = await this.getAllReferences(document, position, token); if (token.isCancellationRequested || !allRefsInfo || !allRefsInfo.references.length) { return undefined; @@ -82,9 +124,44 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide const triggerRef = allRefsInfo.triggerRef; - const isRefRename = triggerRef.kind === 'link' && ( + if (triggerRef.kind === 'link' && ( (triggerRef.link.kind === 'definition' && triggerRef.link.ref.range.contains(position)) || triggerRef.link.href.kind === 'reference' - ); + )) { + return this.renameReferenceLinks(allRefsInfo, newName); + } else if (triggerRef.kind === 'link' && triggerRef.link.href.kind === 'external') { + return this.renameExternalLink(allRefsInfo, newName); + } else if (triggerRef.kind === 'header' || (triggerRef.kind === 'link' && triggerRef.link.source.fragmentRange?.contains(position) && (triggerRef.link.kind === 'definition' || triggerRef.link.kind === 'link' && triggerRef.link.href.kind === 'internal'))) { + return this.renameFragment(allRefsInfo, newName); + } else if (triggerRef.kind === 'link' && !triggerRef.link.source.fragmentRange?.contains(position) && triggerRef.link.kind === 'link' && triggerRef.link.href.kind === 'internal') { + return this.renameFilePath(triggerRef.link.href, allRefsInfo, newName); + } + + return undefined; + } + + private async renameFilePath(triggerHref: InternalHref, allRefsInfo: MdReferencesResponse, newName: string): Promise { + const edit = new vscode.WorkspaceEdit(); + const fileRenames: MdFileRenameEdit[] = []; + + const targetDoc = await tryFindMdDocumentForLink(triggerHref, this.workspaceContents); + const targetUri = targetDoc?.uri ?? triggerHref.path; + const newFilePath = resolveDocumentLink(newName, triggerHref.path); + + // First rename the file + fileRenames.push({ from: targetUri.toString(), to: newFilePath.toString() }); + edit.renameFile(targetUri, newFilePath); + + // Then update all refs to it + for (const ref of allRefsInfo.references) { + if (ref.kind === 'link') { + edit.replace(ref.link.source.resource, this.getFilePathRange(ref), encodeURI(newName)); + } + } + + return { edit, fileRenames }; + } + + private renameFragment(allRefsInfo: MdReferencesResponse, newName: string): MdWorkspaceEdit { const slug = this.slugifier.fromHeading(newName).value; const edit = new vscode.WorkspaceEdit(); @@ -95,22 +172,38 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide break; case 'link': - if (ref.link.kind === 'definition') { - // We may be renaming either the reference or the definition itself - if (isRefRename) { - edit.replace(ref.link.source.resource, ref.link.ref.range, newName); - continue; - } - } - edit.replace(ref.link.source.resource, ref.link.source.fragmentRange ?? ref.location.range, isRefRename && !ref.link.source.fragmentRange || ref.link.href.kind === 'external' ? newName : slug); + edit.replace(ref.link.source.resource, ref.link.source.fragmentRange ?? ref.location.range, !ref.link.source.fragmentRange || ref.link.href.kind === 'external' ? newName : slug); break; } } - - return edit; + return { edit }; } - private async getAllReferences(document: SkinnyTextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise<{ references: MdReference[]; triggerRef: MdReference } | undefined> { + private renameExternalLink(allRefsInfo: MdReferencesResponse, newName: string): MdWorkspaceEdit { + const edit = new vscode.WorkspaceEdit(); + for (const ref of allRefsInfo.references) { + if (ref.kind === 'link') { + edit.replace(ref.link.source.resource, ref.location.range, newName); + } + } + return { edit }; + } + + private renameReferenceLinks(allRefsInfo: MdReferencesResponse, newName: string): MdWorkspaceEdit { + const edit = new vscode.WorkspaceEdit(); + for (const ref of allRefsInfo.references) { + if (ref.kind === 'link') { + if (ref.link.kind === 'definition') { + edit.replace(ref.link.source.resource, ref.link.ref.range, newName); + } else { + edit.replace(ref.link.source.resource, ref.link.source.fragmentRange ?? ref.location.range, newName); + } + } + } + return { edit }; + } + + private async getAllReferences(document: SkinnyTextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { const version = document.version; if (this.cachedRefs diff --git a/extensions/markdown-language-features/src/test/references.test.ts b/extensions/markdown-language-features/src/test/references.test.ts index 45925ffddf9..287612a2154 100644 --- a/extensions/markdown-language-features/src/test/references.test.ts +++ b/extensions/markdown-language-features/src/test/references.test.ts @@ -164,7 +164,7 @@ suite('markdown: find all references', () => { ); }); - test('Should find references from link definition', async () => { + test('Should find header references from link definition', async () => { const uri = workspacePath('doc.md'); const doc = new InMemoryDocument(uri, joinLines( `# A b C`, @@ -466,6 +466,28 @@ suite('markdown: find all references', () => { } }); + test('Should support finding references to unknown file', async () => { + const uri1 = workspacePath('doc1.md'); + const doc1 = new InMemoryDocument(uri1, joinLines( + `![img](/images/more/image.png)`, + ``, + `[ref]: /images/more/image.png`, + )); + + const uri2 = workspacePath('sub', 'doc2.md'); + const doc2 = new InMemoryDocument(uri2, joinLines( + `![img](/images/more/image.png)`, + )); + + + const refs = await getReferences(doc1, new vscode.Position(0, 10), new InMemoryWorkspaceMarkdownDocuments([doc1, doc2])); + assertReferencesEqual(refs!, + { uri: uri1, line: 0 }, + { uri: uri1, line: 2 }, + { uri: uri2, line: 0 }, + ); + }); + suite('Reference links', () => { test('Should find reference links within file from link', async () => { const docUri = workspacePath('doc.md'); diff --git a/extensions/markdown-language-features/src/test/rename.test.ts b/extensions/markdown-language-features/src/test/rename.test.ts index 63aabb2d320..5d57ae83139 100644 --- a/extensions/markdown-language-features/src/test/rename.test.ts +++ b/extensions/markdown-language-features/src/test/rename.test.ts @@ -8,7 +8,7 @@ import 'mocha'; import * as vscode from 'vscode'; import { MdLinkProvider } from '../languageFeatures/documentLinkProvider'; import { MdReferencesProvider } from '../languageFeatures/references'; -import { MdRenameProvider } from '../languageFeatures/rename'; +import { MdRenameProvider, MdWorkspaceEdit } from '../languageFeatures/rename'; import { githubSlugifier } from '../slugify'; import { InMemoryDocument } from '../util/inMemoryDocument'; import { MdWorkspaceContents } from '../workspaceContents'; @@ -24,37 +24,62 @@ function prepareRename(doc: InMemoryDocument, pos: vscode.Position, workspaceCon const engine = createNewMarkdownEngine(); const linkProvider = new MdLinkProvider(engine); const referencesProvider = new MdReferencesProvider(linkProvider, workspaceContents, engine, githubSlugifier); - const renameProvider = new MdRenameProvider(referencesProvider, githubSlugifier); + const renameProvider = new MdRenameProvider(referencesProvider, workspaceContents, 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) { +function getRenameEdits(doc: InMemoryDocument, pos: vscode.Position, newName: string, workspaceContents: MdWorkspaceContents): Promise { const engine = createNewMarkdownEngine(); const linkProvider = new MdLinkProvider(engine); const referencesProvider = new MdReferencesProvider(linkProvider, workspaceContents, engine, githubSlugifier); - const renameProvider = new MdRenameProvider(referencesProvider, githubSlugifier); - return renameProvider.provideRenameEdits(doc, pos, newName, noopToken); + const renameProvider = new MdRenameProvider(referencesProvider, workspaceContents, githubSlugifier); + return renameProvider.provideRenameEditsImpl(doc, pos, newName, noopToken); } -function assertEditsEqual(actualEdit: vscode.WorkspaceEdit, ...expectedEdits: { uri: vscode.Uri; edits: vscode.TextEdit[] }[]) { - const actualEntries = actualEdit.entries(); - assert.strictEqual(actualEntries.length, expectedEdits.length, `Reference counts should match`); +interface ExpectedTextEdit { + readonly uri: vscode.Uri; + readonly edits: readonly vscode.TextEdit[]; +} - for (let i = 0; i < actualEntries.length; ++i) { - const actual = actualEntries[i]; - const expected = expectedEdits[i]; - assert.strictEqual(actual[0].toString(), expected.uri.toString(), `Ref '${i}' has expected document`); +interface ExpectedFileRename { + readonly originalUri: vscode.Uri; + readonly newUri: vscode.Uri; +} - const actualEditForDoc = actual[1]; - const expectedEditsForDoc = expected.edits; - assert.strictEqual(actualEditForDoc.length, expectedEditsForDoc.length, `Edit counts for '${actual[0]}' should match`); +function assertEditsEqual(actualEdit: MdWorkspaceEdit, ...expectedEdits: ReadonlyArray) { + // Check file renames + const expectedFileRenames = expectedEdits.filter(expected => 'originalUri' in expected) as ExpectedFileRename[]; + const actualFileRenames = actualEdit.fileRenames ?? []; + assert.strictEqual(actualFileRenames.length, expectedFileRenames.length, `File rename count should match`); + for (let i = 0; i < actualFileRenames.length; ++i) { + const expected = expectedFileRenames[i]; + const actual = actualFileRenames[i]; + assert.strictEqual(actual.from.toString(), expected.originalUri.toString(), `File rename '${i}' should have expected 'from' resource`); + assert.strictEqual(actual.to.toString(), expected.newUri.toString(), `File rename '${i}' should have expected 'to' resource`); + } - for (let g = 0; g < actualEditForDoc.length; ++g) { - assertRangeEqual(actualEditForDoc[g].range, expectedEditsForDoc[g].range, `Edit '${g}' of '${actual[0]}' has expected expected range. Expected range: ${JSON.stringify(actualEditForDoc[g].range)}. Actual range: ${JSON.stringify(expectedEditsForDoc[g].range)}`); - assert.strictEqual(actualEditForDoc[g].newText, expectedEditsForDoc[g].newText, `Edit '${g}' of '${actual[0]}' has expected edits`); + // Check text edits + const actualTextEdits = actualEdit.edit.entries(); + const expectedTextEdits = expectedEdits.filter(expected => 'edits' in expected) as ExpectedTextEdit[]; + assert.strictEqual(actualTextEdits.length, expectedTextEdits.length, `Reference counts should match`); + for (let i = 0; i < actualTextEdits.length; ++i) { + const expected = expectedTextEdits[i]; + const actual = actualTextEdits[i]; + + if ('edits' in expected) { + assert.strictEqual(actual[0].toString(), expected.uri.toString(), `Ref '${i}' has expected document`); + + const actualEditForDoc = actual[1]; + const expectedEditsForDoc = expected.edits; + assert.strictEqual(actualEditForDoc.length, expectedEditsForDoc.length, `Edit counts for '${actual[0]}' should match`); + + for (let g = 0; g < actualEditForDoc.length; ++g) { + assertRangeEqual(actualEditForDoc[g].range, expectedEditsForDoc[g].range, `Edit '${g}' of '${actual[0]}' has expected expected range. Expected range: ${JSON.stringify(actualEditForDoc[g].range)}. Actual range: ${JSON.stringify(expectedEditsForDoc[g].range)}`); + assert.strictEqual(actualEditForDoc[g].newText, expectedEditsForDoc[g].newText, `Edit '${g}' of '${actual[0]}' has expected edits`); + } } } } @@ -325,24 +350,117 @@ suite('markdown: rename', () => { await assert.rejects(prepareRename(doc, new vscode.Position(1, 2), new InMemoryWorkspaceMarkdownDocuments([doc]))); }); - test('Rename should not be supported on bare file link', async () => { - const uri = workspacePath('doc.md'); - const doc = new InMemoryDocument(uri, joinLines( - `[text](./doc.md)`, - `[other](./doc.md)`, - )); - - await assert.rejects(prepareRename(doc, new vscode.Position(0, 10), new InMemoryWorkspaceMarkdownDocuments([doc]))); - }); - - test('Rename should not be supported on bare file link in definition', async () => { + test('Path rename should use file path as range', async () => { const uri = workspacePath('doc.md'); const doc = new InMemoryDocument(uri, joinLines( `[text](./doc.md)`, `[ref]: ./doc.md`, )); - await assert.rejects(prepareRename(doc, new vscode.Position(1, 10), new InMemoryWorkspaceMarkdownDocuments([doc]))); + const info = await prepareRename(doc, new vscode.Position(0, 10), new InMemoryWorkspaceMarkdownDocuments([doc])); + assert.strictEqual(info!.placeholder, './doc.md'); + assertRangeEqual(info!.range, new vscode.Range(0, 7, 0, 15)); + }); + + test('Path rename\'s range should excludes fragment', async () => { + const uri = workspacePath('doc.md'); + const doc = new InMemoryDocument(uri, joinLines( + `[text](./doc.md#some-header)`, + `[ref]: ./doc.md#some-header`, + )); + + const info = await prepareRename(doc, new vscode.Position(0, 10), new InMemoryWorkspaceMarkdownDocuments([doc])); + assert.strictEqual(info!.placeholder, './doc.md'); + assertRangeEqual(info!.range, new vscode.Range(0, 7, 0, 15)); + }); + + test('Path rename should update file and all refs', async () => { + const uri = workspacePath('doc.md'); + const doc = new InMemoryDocument(uri, joinLines( + `[text](./doc.md)`, + `[ref]: ./doc.md`, + )); + + const edit = await getRenameEdits(doc, new vscode.Position(0, 10), './sub/newDoc.md', new InMemoryWorkspaceMarkdownDocuments([doc])); + assertEditsEqual(edit!, { + originalUri: uri, + newUri: workspacePath('sub', 'newDoc.md'), + }, { + uri: uri, edits: [ + new vscode.TextEdit(new vscode.Range(0, 7, 0, 15), './sub/newDoc.md'), + new vscode.TextEdit(new vscode.Range(1, 7, 1, 15), './sub/newDoc.md'), + ] + }); + }); + + test('Path rename using absolute file path should anchor to workspace root', async () => { + const uri = workspacePath('sub', 'doc.md'); + const doc = new InMemoryDocument(uri, joinLines( + `[text](/sub/doc.md)`, + `[ref]: /sub/doc.md`, + )); + + const edit = await getRenameEdits(doc, new vscode.Position(0, 10), '/newSub/newDoc.md', new InMemoryWorkspaceMarkdownDocuments([doc])); + assertEditsEqual(edit!, { + originalUri: uri, + newUri: workspacePath('newSub', 'newDoc.md'), + }, { + uri: uri, edits: [ + new vscode.TextEdit(new vscode.Range(0, 7, 0, 18), '/newSub/newDoc.md'), + new vscode.TextEdit(new vscode.Range(1, 7, 1, 18), '/newSub/newDoc.md'), + ] + }); + }); + + test('Path rename should encode paths', async () => { + const uri = workspacePath('sub', 'doc.md'); + const doc = new InMemoryDocument(uri, joinLines( + `[text](/sub/doc.md)`, + `[ref]: /sub/doc.md`, + )); + + const edit = await getRenameEdits(doc, new vscode.Position(0, 10), '/NEW sub/new DOC.md', new InMemoryWorkspaceMarkdownDocuments([doc])); + assertEditsEqual(edit!, { + originalUri: uri, + newUri: workspacePath('NEW sub', 'new DOC.md'), + }, { + uri: uri, edits: [ + new vscode.TextEdit(new vscode.Range(0, 7, 0, 18), '/NEW%20sub/new%20DOC.md'), + new vscode.TextEdit(new vscode.Range(1, 7, 1, 18), '/NEW%20sub/new%20DOC.md'), + ] + }); + }); + + test('Path rename should work with unknown files', async () => { + const uri1 = workspacePath('doc1.md'); + const doc1 = new InMemoryDocument(uri1, joinLines( + `![img](/images/more/image.png)`, + ``, + `[ref]: /images/more/image.png`, + )); + + const uri2 = workspacePath('sub', 'doc2.md'); + const doc2 = new InMemoryDocument(uri2, joinLines( + `![img](/images/more/image.png)`, + )); + + const edit = await getRenameEdits(doc1, new vscode.Position(0, 10), '/img/test/new.png', new InMemoryWorkspaceMarkdownDocuments([ + doc1, + doc2 + ])); + assertEditsEqual(edit!, { + originalUri: workspacePath('images', 'more', 'image.png'), + newUri: workspacePath('img', 'test', 'new.png'), + }, { + uri: uri1, edits: [ + new vscode.TextEdit(new vscode.Range(0, 7, 0, 29), '/img/test/new.png'), + new vscode.TextEdit(new vscode.Range(2, 7, 2, 29), '/img/test/new.png'), + ] + }, { + uri: uri2, edits: [ + new vscode.TextEdit(new vscode.Range(0, 7, 0, 29), '/img/test/new.png'), + ] + }); }); test('Rename on link should use header text as placeholder', async () => { @@ -357,7 +475,7 @@ suite('markdown: rename', () => { assertRangeEqual(info!.range, new vscode.Range(1, 8, 1, 13)); }); - test('Rename on http uri should work ', async () => { + test('Rename on http uri should work', async () => { const uri1 = workspacePath('doc.md'); const uri2 = workspacePath('doc2.md'); const doc = new InMemoryDocument(uri1, joinLines( From 0610f195fb3d789637d7d6ae15735613191e3c9f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 20 Apr 2022 16:46:30 -0700 Subject: [PATCH 218/245] Try automatically adding .md file extension to new paths on rename --- .../src/languageFeatures/rename.ts | 13 ++++++++++++- .../src/test/rename.test.ts | 19 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/src/languageFeatures/rename.ts b/extensions/markdown-language-features/src/languageFeatures/rename.ts index 9bad2489ab9..55a723d8db4 100644 --- a/extensions/markdown-language-features/src/languageFeatures/rename.ts +++ b/extensions/markdown-language-features/src/languageFeatures/rename.ts @@ -10,6 +10,7 @@ import { resolveDocumentLink } from '../util/openDocumentLink'; import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; import { InternalHref } from './documentLinkProvider'; import { MdHeaderReference, MdLinkReference, MdReference, MdReferencesProvider, tryFindMdDocumentForLink } from './references'; +import * as URI from 'vscode-uri'; const localize = nls.loadMessageBundle(); @@ -145,7 +146,17 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide const targetDoc = await tryFindMdDocumentForLink(triggerHref, this.workspaceContents); const targetUri = targetDoc?.uri ?? triggerHref.path; - const newFilePath = resolveDocumentLink(newName, triggerHref.path); + + let newFilePath = resolveDocumentLink(newName, triggerHref.path); + if (!URI.Utils.extname(newFilePath)) { + // If the newly entered path doesn't have a file extension but the original file did + // tack on a .md file extension + if (URI.Utils.extname(targetUri)) { + newFilePath = newFilePath.with({ + path: newFilePath.path + '.md' + }); + } + } // First rename the file fileRenames.push({ from: targetUri.toString(), to: newFilePath.toString() }); diff --git a/extensions/markdown-language-features/src/test/rename.test.ts b/extensions/markdown-language-features/src/test/rename.test.ts index 5d57ae83139..8ad8672edc8 100644 --- a/extensions/markdown-language-features/src/test/rename.test.ts +++ b/extensions/markdown-language-features/src/test/rename.test.ts @@ -463,6 +463,25 @@ suite('markdown: rename', () => { }); }); + test('Path rename should use .md extension on extension-less link', async () => { + const uri = workspacePath('doc.md'); + const doc = new InMemoryDocument(uri, joinLines( + `[text](/doc#header)`, + `[ref]: /doc#other`, + )); + + const edit = await getRenameEdits(doc, new vscode.Position(0, 10), '/new File', new InMemoryWorkspaceMarkdownDocuments([doc])); + assertEditsEqual(edit!, { + originalUri: uri, + newUri: workspacePath('new File.md'), + }, { + uri: uri, edits: [ + new vscode.TextEdit(new vscode.Range(0, 7, 0, 11), '/new%20File'), + new vscode.TextEdit(new vscode.Range(1, 7, 1, 11), '/new%20File'), + ] + }); + }); + test('Rename on link should use header text as placeholder', async () => { const uri = workspacePath('doc.md'); const doc = new InMemoryDocument(uri, joinLines( From b438e8438d6e57260697ec56532a718f0aa76ec6 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 20 Apr 2022 17:36:47 -0700 Subject: [PATCH 219/245] Fixing rename to use correct file paths relative to containing files --- .../src/languageFeatures/rename.ts | 29 +++++++++---- .../src/test/rename.test.ts | 42 ++++++++++++++++++- 2 files changed, 61 insertions(+), 10 deletions(-) diff --git a/extensions/markdown-language-features/src/languageFeatures/rename.ts b/extensions/markdown-language-features/src/languageFeatures/rename.ts index 55a723d8db4..716f8e41651 100644 --- a/extensions/markdown-language-features/src/languageFeatures/rename.ts +++ b/extensions/markdown-language-features/src/languageFeatures/rename.ts @@ -2,15 +2,16 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as path from 'path'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; +import * as URI from 'vscode-uri'; import { Slugifier } from '../slugify'; 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 * as URI from 'vscode-uri'; const localize = nls.loadMessageBundle(); @@ -147,25 +148,37 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide const targetDoc = await tryFindMdDocumentForLink(triggerHref, this.workspaceContents); const targetUri = targetDoc?.uri ?? triggerHref.path; - let newFilePath = resolveDocumentLink(newName, triggerHref.path); - if (!URI.Utils.extname(newFilePath)) { + let rawNewFilePath = resolveDocumentLink(newName, triggerHref.path); + let resolvedNewFilePath = rawNewFilePath; + if (!URI.Utils.extname(resolvedNewFilePath)) { // If the newly entered path doesn't have a file extension but the original file did // tack on a .md file extension if (URI.Utils.extname(targetUri)) { - newFilePath = newFilePath.with({ - path: newFilePath.path + '.md' + resolvedNewFilePath = resolvedNewFilePath.with({ + path: resolvedNewFilePath.path + '.md' }); } } // First rename the file - fileRenames.push({ from: targetUri.toString(), to: newFilePath.toString() }); - edit.renameFile(targetUri, newFilePath); + fileRenames.push({ from: targetUri.toString(), to: resolvedNewFilePath.toString() }); + edit.renameFile(targetUri, resolvedNewFilePath); // Then update all refs to it for (const ref of allRefsInfo.references) { if (ref.kind === 'link') { - edit.replace(ref.link.source.resource, this.getFilePathRange(ref), encodeURI(newName)); + // Try to preserve style of existing links + let newPath: string; + if (ref.link.source.text.startsWith('/')) { + const root = resolveDocumentLink('/', ref.link.source.resource); + newPath = '/' + path.relative(root.toString(true), rawNewFilePath.toString(true)); + } else { + newPath = path.relative(URI.Utils.dirname(ref.link.source.resource).toString(true), rawNewFilePath.toString(true)); + if (newName.startsWith('./') && !newPath.startsWith('../')) { + newPath = './' + newPath; + } + } + edit.replace(ref.link.source.resource, this.getFilePathRange(ref), encodeURI(newPath)); } } diff --git a/extensions/markdown-language-features/src/test/rename.test.ts b/extensions/markdown-language-features/src/test/rename.test.ts index 8ad8672edc8..e24405012af 100644 --- a/extensions/markdown-language-features/src/test/rename.test.ts +++ b/extensions/markdown-language-features/src/test/rename.test.ts @@ -473,15 +473,53 @@ suite('markdown: rename', () => { const edit = await getRenameEdits(doc, new vscode.Position(0, 10), '/new File', new InMemoryWorkspaceMarkdownDocuments([doc])); assertEditsEqual(edit!, { originalUri: uri, - newUri: workspacePath('new File.md'), + newUri: workspacePath('new File.md'), // Rename on disk should use file extension }, { uri: uri, edits: [ - new vscode.TextEdit(new vscode.Range(0, 7, 0, 11), '/new%20File'), + new vscode.TextEdit(new vscode.Range(0, 7, 0, 11), '/new%20File'), // Links should continue to use extension-less paths new vscode.TextEdit(new vscode.Range(1, 7, 1, 11), '/new%20File'), ] }); }); + test('Path rename should use correctly resolved paths across files', async () => { + const uri1 = workspacePath('sub', 'doc.md'); + const doc1 = new InMemoryDocument(uri1, joinLines( + `[text](./doc.md)`, + )); + + const uri2 = workspacePath('doc2.md'); + const doc2 = new InMemoryDocument(uri2, joinLines( + `[text](./sub/doc.md)`, + )); + + const uri3 = workspacePath('sub2', 'doc3.md'); + const doc3 = new InMemoryDocument(uri3, joinLines( + `[text](../sub/doc.md)`, + )); + + const uri4 = workspacePath('sub2', 'doc4.md'); + const doc4 = new InMemoryDocument(uri4, joinLines( + `[text](/sub/doc.md)`, + )); + + const edit = await getRenameEdits(doc1, new vscode.Position(0, 10), './new/new-doc.md', new InMemoryWorkspaceMarkdownDocuments([ + doc1, doc2, doc3, doc4, + ])); + assertEditsEqual(edit!, { + originalUri: uri1, + newUri: workspacePath('sub', 'new', 'new-doc.md'), + }, { + uri: uri1, edits: [new vscode.TextEdit(new vscode.Range(0, 7, 0, 15), './new/new-doc.md')] + }, { + uri: uri2, edits: [new vscode.TextEdit(new vscode.Range(0, 7, 0, 19), './sub/new/new-doc.md')] + }, { + uri: uri3, edits: [new vscode.TextEdit(new vscode.Range(0, 7, 0, 20), '../sub/new/new-doc.md')] + }, { + uri: uri4, edits: [new vscode.TextEdit(new vscode.Range(0, 7, 0, 18), '/sub/new/new-doc.md')] + }); + }); + test('Rename on link should use header text as placeholder', async () => { const uri = workspacePath('doc.md'); const doc = new InMemoryDocument(uri, joinLines( From 62eebab685c24b42c4aaae8979d36a06b8b8cacc Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 20 Apr 2022 17:43:02 -0700 Subject: [PATCH 220/245] Use uri instead of string --- .../src/languageFeatures/rename.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/markdown-language-features/src/languageFeatures/rename.ts b/extensions/markdown-language-features/src/languageFeatures/rename.ts index 716f8e41651..a8ce8ebe3af 100644 --- a/extensions/markdown-language-features/src/languageFeatures/rename.ts +++ b/extensions/markdown-language-features/src/languageFeatures/rename.ts @@ -22,8 +22,8 @@ export interface MdReferencesResponse { } interface MdFileRenameEdit { - readonly from: string; - readonly to: string; + readonly from: vscode.Uri; + readonly to: vscode.Uri; } /** @@ -161,7 +161,7 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide } // First rename the file - fileRenames.push({ from: targetUri.toString(), to: resolvedNewFilePath.toString() }); + fileRenames.push({ from: targetUri, to: resolvedNewFilePath }); edit.renameFile(targetUri, resolvedNewFilePath); // Then update all refs to it From 7b1168660ac0bb2a7addf8a3cffc5b2045930844 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 20 Apr 2022 18:07:47 -0700 Subject: [PATCH 221/245] Fix resolving of links on rename We should resolve relative to the file the rename is triggered in --- .../src/languageFeatures/rename.ts | 6 +-- .../src/test/rename.test.ts | 48 +++++++++++++++++-- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/extensions/markdown-language-features/src/languageFeatures/rename.ts b/extensions/markdown-language-features/src/languageFeatures/rename.ts index a8ce8ebe3af..8a80906fbad 100644 --- a/extensions/markdown-language-features/src/languageFeatures/rename.ts +++ b/extensions/markdown-language-features/src/languageFeatures/rename.ts @@ -135,20 +135,20 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide } else if (triggerRef.kind === 'header' || (triggerRef.kind === 'link' && triggerRef.link.source.fragmentRange?.contains(position) && (triggerRef.link.kind === 'definition' || triggerRef.link.kind === 'link' && triggerRef.link.href.kind === 'internal'))) { return this.renameFragment(allRefsInfo, newName); } else if (triggerRef.kind === 'link' && !triggerRef.link.source.fragmentRange?.contains(position) && triggerRef.link.kind === 'link' && triggerRef.link.href.kind === 'internal') { - return this.renameFilePath(triggerRef.link.href, allRefsInfo, newName); + return this.renameFilePath(triggerRef.link.source.resource, triggerRef.link.href, allRefsInfo, newName); } return undefined; } - private async renameFilePath(triggerHref: InternalHref, allRefsInfo: MdReferencesResponse, newName: string): Promise { + private async renameFilePath(triggerDocument: vscode.Uri, triggerHref: InternalHref, allRefsInfo: MdReferencesResponse, newName: string): Promise { const edit = new vscode.WorkspaceEdit(); const fileRenames: MdFileRenameEdit[] = []; const targetDoc = await tryFindMdDocumentForLink(triggerHref, this.workspaceContents); const targetUri = targetDoc?.uri ?? triggerHref.path; - let rawNewFilePath = resolveDocumentLink(newName, triggerHref.path); + const rawNewFilePath = resolveDocumentLink(newName, triggerDocument); let resolvedNewFilePath = rawNewFilePath; if (!URI.Utils.extname(resolvedNewFilePath)) { // If the newly entered path doesn't have a file extension but the original file did diff --git a/extensions/markdown-language-features/src/test/rename.test.ts b/extensions/markdown-language-features/src/test/rename.test.ts index e24405012af..e937c76574b 100644 --- a/extensions/markdown-language-features/src/test/rename.test.ts +++ b/extensions/markdown-language-features/src/test/rename.test.ts @@ -486,21 +486,25 @@ suite('markdown: rename', () => { const uri1 = workspacePath('sub', 'doc.md'); const doc1 = new InMemoryDocument(uri1, joinLines( `[text](./doc.md)`, + `[ref]: ./doc.md`, )); const uri2 = workspacePath('doc2.md'); const doc2 = new InMemoryDocument(uri2, joinLines( `[text](./sub/doc.md)`, + `[ref]: ./sub/doc.md`, )); const uri3 = workspacePath('sub2', 'doc3.md'); const doc3 = new InMemoryDocument(uri3, joinLines( `[text](../sub/doc.md)`, + `[ref]: ../sub/doc.md`, )); const uri4 = workspacePath('sub2', 'doc4.md'); const doc4 = new InMemoryDocument(uri4, joinLines( `[text](/sub/doc.md)`, + `[ref]: /sub/doc.md`, )); const edit = await getRenameEdits(doc1, new vscode.Position(0, 10), './new/new-doc.md', new InMemoryWorkspaceMarkdownDocuments([ @@ -510,13 +514,49 @@ suite('markdown: rename', () => { originalUri: uri1, newUri: workspacePath('sub', 'new', 'new-doc.md'), }, { - uri: uri1, edits: [new vscode.TextEdit(new vscode.Range(0, 7, 0, 15), './new/new-doc.md')] + uri: uri1, edits: [ + new vscode.TextEdit(new vscode.Range(0, 7, 0, 15), './new/new-doc.md'), + new vscode.TextEdit(new vscode.Range(1, 7, 1, 15), './new/new-doc.md'), + ] }, { - uri: uri2, edits: [new vscode.TextEdit(new vscode.Range(0, 7, 0, 19), './sub/new/new-doc.md')] + uri: uri2, edits: [ + new vscode.TextEdit(new vscode.Range(0, 7, 0, 19), './sub/new/new-doc.md'), + new vscode.TextEdit(new vscode.Range(1, 7, 1, 19), './sub/new/new-doc.md'), + ] }, { - uri: uri3, edits: [new vscode.TextEdit(new vscode.Range(0, 7, 0, 20), '../sub/new/new-doc.md')] + uri: uri3, edits: [ + new vscode.TextEdit(new vscode.Range(0, 7, 0, 20), '../sub/new/new-doc.md'), + new vscode.TextEdit(new vscode.Range(1, 7, 1, 20), '../sub/new/new-doc.md'), + ] }, { - uri: uri4, edits: [new vscode.TextEdit(new vscode.Range(0, 7, 0, 18), '/sub/new/new-doc.md')] + uri: uri4, edits: [ + new vscode.TextEdit(new vscode.Range(0, 7, 0, 18), '/sub/new/new-doc.md'), + new vscode.TextEdit(new vscode.Range(1, 7, 1, 18), '/sub/new/new-doc.md'), + ] + }); + }); + + test('Path rename should resolve on links without prefix', async () => { + const uri1 = workspacePath('sub', 'doc.md'); + const doc1 = new InMemoryDocument(uri1, joinLines( + `![text](images/cat.gif)`, + )); + + const uri2 = workspacePath('doc2.md'); + const doc2 = new InMemoryDocument(uri2, joinLines( + `![text](sub/images/cat.gif)`, + )); + + const edit = await getRenameEdits(doc1, new vscode.Position(0, 10), 'img/cat.gif', new InMemoryWorkspaceMarkdownDocuments([ + doc1, doc2, + ])); + assertEditsEqual(edit!, { + originalUri: workspacePath('sub', 'images', 'cat.gif'), + newUri: workspacePath('sub', 'img', 'cat.gif'), + }, { + uri: uri1, edits: [new vscode.TextEdit(new vscode.Range(0, 8, 0, 22), 'img/cat.gif')] + }, { + uri: uri2, edits: [new vscode.TextEdit(new vscode.Range(0, 8, 0, 26), 'sub/img/cat.gif')] }); }); From 59b6227bf34fb2a5e4104182c7407504d8a2422d Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 20 Apr 2022 18:16:07 -0700 Subject: [PATCH 222/245] Use un-encoded file paths are rename placeholder --- .../src/languageFeatures/rename.ts | 10 +++++++++- .../markdown-language-features/src/test/rename.test.ts | 10 ++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/src/languageFeatures/rename.ts b/extensions/markdown-language-features/src/languageFeatures/rename.ts index 8a80906fbad..4184741a411 100644 --- a/extensions/markdown-language-features/src/languageFeatures/rename.ts +++ b/extensions/markdown-language-features/src/languageFeatures/rename.ts @@ -37,6 +37,14 @@ export interface MdWorkspaceEdit { readonly fileRenames?: ReadonlyArray; } +function tryDecodeUri(str: string): string { + try { + return decodeURI(str); + } catch { + return str; + } +} + export class MdRenameProvider extends Disposable implements vscode.RenameProvider { private cachedRefs?: { @@ -98,7 +106,7 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide if (!range) { throw new Error(this.renameNotSupportedText); } - return { range, placeholder: document.getText(range) }; + return { range, placeholder: tryDecodeUri(document.getText(range)) }; } } } diff --git a/extensions/markdown-language-features/src/test/rename.test.ts b/extensions/markdown-language-features/src/test/rename.test.ts index e937c76574b..057e734131a 100644 --- a/extensions/markdown-language-features/src/test/rename.test.ts +++ b/extensions/markdown-language-features/src/test/rename.test.ts @@ -412,6 +412,16 @@ suite('markdown: rename', () => { }); }); + test('Path rename should use un-encoded paths as placeholder', async () => { + const uri = workspacePath('sub', 'doc with spaces.md'); + const doc = new InMemoryDocument(uri, joinLines( + `[text](/sub/doc%20with%20spaces.md)`, + )); + + const info = await prepareRename(doc, new vscode.Position(0, 10), new InMemoryWorkspaceMarkdownDocuments([doc])); + assert.strictEqual(info!.placeholder, '/sub/doc with spaces.md'); + }); + test('Path rename should encode paths', async () => { const uri = workspacePath('sub', 'doc.md'); const doc = new InMemoryDocument(uri, joinLines( From 82396b190d1b4db4fa66079deb2526f2f5adb616 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 22 Apr 2022 07:26:58 +0200 Subject: [PATCH 223/245] tests - mark quick input tests flaky (#147543) --- src/vs/base/parts/quickinput/test/browser/quickinput.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/base/parts/quickinput/test/browser/quickinput.test.ts b/src/vs/base/parts/quickinput/test/browser/quickinput.test.ts index 05d5511424c..ea365793c29 100644 --- a/src/vs/base/parts/quickinput/test/browser/quickinput.test.ts +++ b/src/vs/base/parts/quickinput/test/browser/quickinput.test.ts @@ -8,6 +8,7 @@ import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/lis import { IListOptions, List } from 'vs/base/browser/ui/list/listWidget'; import { QuickInputController } from 'vs/base/parts/quickinput/browser/quickInput'; import { IQuickPick, IQuickPickItem } from 'vs/base/parts/quickinput/common/quickInput'; +import { flakySuite } from 'vs/base/test/common/testUtils'; // Simple promisify of setTimeout function wait(delayMS: number) { @@ -16,7 +17,7 @@ function wait(delayMS: number) { }); } -suite('QuickInput', () => { +flakySuite('QuickInput', () => { // https://github.com/microsoft/vscode/issues/147543 let fixture: HTMLElement, controller: QuickInputController, quickpick: IQuickPick; function getScrollTop(): number { From d982c14b664f8b6fcd649a31272066f129227306 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Thu, 21 Apr 2022 23:05:39 -0700 Subject: [PATCH 224/245] fix non windows shell type context key (#147884) --- .../contrib/terminal/browser/terminalInstance.ts | 9 ++++----- .../contrib/terminal/browser/terminalService.ts | 7 +++++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 18be1c0ae4a..0adfa2cee9d 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -460,6 +460,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { // Re-establish the title after reconnect if (this.shellLaunchConfig.attachPersistentProcess) { this.refreshTabLabels(this.shellLaunchConfig.attachPersistentProcess.title, this.shellLaunchConfig.attachPersistentProcess.titleSource); + this.setShellType(this.shellType); } if (this._fixedCols) { @@ -1098,11 +1099,6 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._register(dom.addDisposableListener(xterm.raw.textarea, 'focus', () => { this._terminalFocusContextKey.set(true); - if (this.shellType) { - this._terminalShellTypeContextKey.set(this.shellType.toString()); - } else { - this._terminalShellTypeContextKey.reset(); - } this._onDidFocus.fire(this); })); @@ -1882,6 +1878,9 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { setShellType(shellType: TerminalShellType) { this._shellType = shellType; + if (shellType) { + this._terminalShellTypeContextKey.set(shellType?.toString()); + } } private _setAriaLabel(xterm: XTermTerminal | undefined, terminalId: number, title: string | undefined): void { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 8402e0e5362..62d52e82cc9 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -52,6 +52,7 @@ export class TerminalService implements ITerminalService { private _hostActiveTerminals: Map = new Map(); private _terminalEditorActive: IContextKey; + private readonly _terminalShellTypeContextKey: IContextKey; private _escapeSequenceLoggingEnabled: boolean = false; @@ -187,9 +188,15 @@ export class TerminalService implements ITerminalService { if (!instance && !this._isShuttingDown) { this._terminalGroupService.hidePanel(); } + if (instance?.shellType) { + this._terminalShellTypeContextKey.set(instance.shellType.toString()); + } else if (!instance) { + this._terminalShellTypeContextKey.reset(); + } }); this._handleInstanceContextKeys(); + this._terminalShellTypeContextKey = TerminalContextKeys.shellType.bindTo(this._contextKeyService); this._processSupportContextKey = TerminalContextKeys.processSupported.bindTo(this._contextKeyService); this._processSupportContextKey.set(!isWeb || this._remoteAgentService.getConnection() !== null); this._terminalHasBeenCreated = TerminalContextKeys.terminalHasBeenCreated.bindTo(this._contextKeyService); From 72b4be55fad0d41092d7df8e03b5597c715fd2e2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 22 Apr 2022 08:07:32 +0200 Subject: [PATCH 225/245] skip markdown rename tests (#147896) --- extensions/markdown-language-features/src/test/rename.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/src/test/rename.test.ts b/extensions/markdown-language-features/src/test/rename.test.ts index 057e734131a..aedad07d917 100644 --- a/extensions/markdown-language-features/src/test/rename.test.ts +++ b/extensions/markdown-language-features/src/test/rename.test.ts @@ -84,7 +84,7 @@ function assertEditsEqual(actualEdit: MdWorkspaceEdit, ...expectedEdits: Readonl } } -suite('markdown: rename', () => { +suite.skip('markdown: rename', () => { // TODO@mjbvz https://github.com/microsoft/vscode/issues/c setup(async () => { // the tests make the assumption that link providers are already registered From ff3d5bb4911dbfd3f3f2851d352132bea32a71e3 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 22 Apr 2022 08:54:40 +0200 Subject: [PATCH 226/245] debt - adopt new `options` support in `IPath` cc @rebornix --- src/vs/platform/window/common/window.ts | 48 +++++++----------- .../electron-main/windowsMainService.ts | 49 +++++++++++++------ src/vs/workbench/browser/layout.ts | 21 +++++--- src/vs/workbench/browser/web.api.ts | 28 ++++++----- src/vs/workbench/common/editor.ts | 6 +-- .../environment/browser/environmentService.ts | 7 ++- .../host/browser/browserHostService.ts | 7 ++- 7 files changed, 92 insertions(+), 74 deletions(-) diff --git a/src/vs/platform/window/common/window.ts b/src/vs/platform/window/common/window.ts index dfa18a3fcda..2c9591a2bec 100644 --- a/src/vs/platform/window/common/window.ts +++ b/src/vs/platform/window/common/window.ts @@ -168,54 +168,44 @@ export function getTitleBarStyle(configurationService: IConfigurationService): ' return isLinux ? 'native' : 'custom'; // default to custom on all macOS and Windows } -export interface IPath extends IPathData { +export interface IPath extends IPathData { - // the file path to open within the instance + /** + * The file path to open within the instance + */ fileUri?: URI; } -export interface IPathData { +export interface IPathData { - // the file path to open within the instance + /** + * The file path to open within the instance + */ readonly fileUri?: UriComponents; /** * Optional editor options to apply in the file */ - readonly options?: IEditorOptions; + readonly options?: T; /** - * An optional selection to apply in the file - * - * @deprecated Use options instead + * A hint that the file exists. if true, the + * file exists, if false it does not. with + * `undefined` the state is unknown. */ - readonly selection?: { - readonly startLineNumber: number; - readonly startColumn: number; - readonly endLineNumber?: number; - readonly endColumn?: number; - }; - - // a hint that the file exists. if true, the - // file exists, if false it does not. with - // `undefined` the state is unknown. readonly exists?: boolean; - // a hint about the file type of this path. - // with `undefined` the type is unknown. + /** + * A hint about the file type of this path. + * with `undefined` the type is unknown. + */ readonly type?: FileType; - // Specifies if the file should be only be opened - // if it exists - readonly openOnlyIfExists?: boolean; - /** - * Specifies an optional id to override the editor - * used to edit the resource, e.g. custom editor. - * - * @deprecated Use options.override instead + * Specifies if the file should be only be opened + * if it exists. */ - readonly editorOverrideId?: string; + readonly openOnlyIfExists?: boolean; } export interface IPathsToWaitFor extends IPathsToWaitForData { diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 1dbfaaf9df9..77294070103 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -50,6 +50,7 @@ import { IWorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-m import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService'; import { ICodeWindow, UnloadReason } from 'vs/platform/window/electron-main/window'; import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; +import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/editor'; //#region Helper Interfaces @@ -118,23 +119,33 @@ interface IFilesToOpen { filesToWait?: IPathsToWaitFor; } -interface IPathToOpen extends IPath { +interface IPathToOpen extends IPath { - // the workspace to open + /** + * The workspace to open + */ readonly workspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier; - // whether the path is considered to be transient or not - // for example, a transient workspace should not add to - // the workspaces history and should never restore + /** + * Whether the path is considered to be transient or not + * for example, a transient workspace should not add to + * the workspaces history and should never restore. + */ readonly transient?: boolean; - // the backup path to use + /** + * The backup path to use + */ readonly backupPath?: string; - // the remote authority for the Code instance to open. Undefined if not remote. + /** + * The remote authority for the Code instance to open. Undefined if not remote. + */ readonly remoteAuthority?: string; - // optional label for the recent history + /** + * Optional label for the recent history + */ label?: string; } @@ -916,7 +927,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic return this.doResolveRemoteOpenable(openable, options); } - private doResolveRemoteOpenable(openable: IWindowOpenable, options: IPathResolveOptions): IPathToOpen | undefined { + private doResolveRemoteOpenable(openable: IWindowOpenable, options: IPathResolveOptions): IPathToOpen | undefined { let uri = this.resourceFromOpenable(openable); // use remote authority from vscode @@ -932,7 +943,9 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic return { fileUri: uri.with({ path }), - selection: line ? { startLineNumber: line, startColumn: column || 1 } : undefined, + options: { + selection: line ? { startLineNumber: line, startColumn: column || 1 } : undefined + }, remoteAuthority }; } @@ -961,7 +974,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic return openable.fileUri; } - private doResolveFilePath(path: string, options: IPathResolveOptions): IPathToOpen | undefined { + private doResolveFilePath(path: string, options: IPathResolveOptions): IPathToOpen | undefined { // Extract line/col information from path let lineNumber: number | undefined; @@ -1004,7 +1017,9 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic fileUri: URI.file(path), type: FileType.File, exists: true, - selection: lineNumber ? { startLineNumber: lineNumber, startColumn: columnNumber || 1 } : undefined + options: { + selection: lineNumber ? { startLineNumber: lineNumber, startColumn: columnNumber || 1 } : undefined + } }; } @@ -1047,7 +1062,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic return undefined; } - private doResolvePathRemote(path: string, options: IPathResolveOptions): IPathToOpen | undefined { + private doResolvePathRemote(path: string, options: IPathResolveOptions): IPathToOpen | undefined { const first = path.charCodeAt(0); const remoteAuthority = options.remoteAuthority; @@ -1081,7 +1096,9 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic if (options.forceOpenWorkspaceAsFile) { return { fileUri: uri, - selection: lineNumber ? { startLineNumber: lineNumber, startColumn: columnNumber || 1 } : undefined, + options: { + selection: lineNumber ? { startLineNumber: lineNumber, startColumn: columnNumber || 1 } : undefined + }, remoteAuthority: options.remoteAuthority }; } @@ -1093,7 +1110,9 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic else if (options.gotoLineMode || posix.basename(path).indexOf('.') !== -1) { return { fileUri: uri, - selection: lineNumber ? { startLineNumber: lineNumber, startColumn: columnNumber || 1 } : undefined, + options: { + selection: lineNumber ? { startLineNumber: lineNumber, startColumn: columnNumber || 1 } : undefined + }, remoteAuthority }; } diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 8ab1929c10f..1309b3de8e1 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -617,17 +617,22 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi return { filesToOpenOrCreate: defaultLayout.editors.map(file => { + const legacyOverride = file.openWith; + const legacySelection = file.selection && file.selection.start && isNumber(file.selection.start.line) ? { + startLineNumber: file.selection.start.line, + startColumn: isNumber(file.selection.start.column) ? file.selection.start.column : 1, + endLineNumber: isNumber(file.selection.end.line) ? file.selection.end.line : undefined, + endColumn: isNumber(file.selection.end.line) ? (isNumber(file.selection.end.column) ? file.selection.end.column : 1) : undefined, + } : undefined; + return { fileUri: URI.revive(file.uri), - selection: file.selection && file.selection.start && isNumber(file.selection.start.line) ? { - startLineNumber: file.selection.start.line, - startColumn: isNumber(file.selection.start.column) ? file.selection.start.column : 1, - endLineNumber: isNumber(file.selection.end.line) ? file.selection.end.line : undefined, - endColumn: isNumber(file.selection.end.line) ? (isNumber(file.selection.end.column) ? file.selection.end.column : 1) : undefined, - } : undefined, openOnlyIfExists: file.openOnlyIfExists, - editorOverrideId: file.openWith, - options: file.options + options: { + selection: legacySelection, + override: legacyOverride, + ...file.options // keep at the end to override legacy selection/override that may be `undefined` + } }; }) }; diff --git a/src/vs/workbench/browser/web.api.ts b/src/vs/workbench/browser/web.api.ts index 9ddbf748e47..effacb4a75f 100644 --- a/src/vs/workbench/browser/web.api.ts +++ b/src/vs/workbench/browser/web.api.ts @@ -561,39 +561,42 @@ export interface IDefaultView { readonly id: string; } +/** + * @deprecated use `IDefaultEditor.options` instead + */ export interface IPosition { readonly line: number; readonly column: number; } +/** + * @deprecated use `IDefaultEditor.options` instead + */ export interface IRange { - - /** - * The start position. It is before or equal to end position. - */ readonly start: IPosition; - - /** - * The end position. It is after or equal to start position. - */ readonly end: IPosition; } export interface IDefaultEditor { + readonly uri: UriComponents; + readonly options?: IEditorOptions; + + readonly openOnlyIfExists?: boolean; + /** - * @deprecated Use options instead + * @deprecated use `options` instead */ readonly selection?: IRange; - readonly openOnlyIfExists?: boolean; + /** - * @deprecated Use options.override instead + * @deprecated use `options.override` instead */ readonly openWith?: string; - readonly options?: IEditorOptions; } export interface IDefaultLayout { + readonly views?: IDefaultView[]; readonly editors?: IDefaultEditor[]; @@ -657,4 +660,3 @@ export interface IDevelopmentOptions { */ readonly enableSmokeTestDriver?: boolean; } - diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 3c6225571ae..b12e6477782 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -1342,11 +1342,7 @@ export async function pathsToEditors(paths: IPathData[] | undefined, fileService const options: IEditorOptions = { ...path.options, - ...{ - selection: exists ? path.selection : undefined - }, - pinned: true, - override: path.editorOverrideId + pinned: true }; let input: IResourceEditorInput | IUntitledTextResourceEditorInput; diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index a5499a1ef74..d41839b87ba 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -17,6 +17,7 @@ import { parseLineAndColumnAware } from 'vs/base/common/extpath'; import { LogLevelToString } from 'vs/platform/log/common/log'; import { isUndefined } from 'vs/base/common/types'; import { refineServiceDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; export const IBrowserWorkbenchEnvironmentService = refineServiceDecorator(IEnvironmentService); @@ -295,7 +296,7 @@ export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvi } @memoize - get filesToOpenOrCreate(): IPath[] | undefined { + get filesToOpenOrCreate(): IPath[] | undefined { if (this.payload) { const fileToOpen = this.payload.get('openFile'); if (fileToOpen) { @@ -307,7 +308,9 @@ export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvi return [{ fileUri: fileUri.with({ path: pathColumnAware.path }), - selection: !isUndefined(pathColumnAware.line) ? { startLineNumber: pathColumnAware.line, startColumn: pathColumnAware.column || 1 } : undefined + options: { + selection: !isUndefined(pathColumnAware.line) ? { startLineNumber: pathColumnAware.line, startColumn: pathColumnAware.column || 1 } : undefined + } }]; } diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index cdc63895e27..955ba20bddf 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -34,6 +34,7 @@ import { isUndefined } from 'vs/base/common/types'; import { isTemporaryWorkspace, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { Schemas } from 'vs/base/common/network'; +import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; /** * A workspace to open in the workbench can either be: @@ -285,14 +286,16 @@ export class BrowserHostService extends Disposable implements IHostService { // Same Window: open via editor service in current window if (this.shouldReuse(options, true /* file */)) { - let openables: IPathData[] = []; + let openables: IPathData[] = []; // Support: --goto parameter to open on line/col if (options?.gotoLineMode) { const pathColumnAware = parseLineAndColumnAware(openable.fileUri.path); openables = [{ fileUri: openable.fileUri.with({ path: pathColumnAware.path }), - selection: !isUndefined(pathColumnAware.line) ? { startLineNumber: pathColumnAware.line, startColumn: pathColumnAware.column || 1 } : undefined + options: { + selection: !isUndefined(pathColumnAware.line) ? { startLineNumber: pathColumnAware.line, startColumn: pathColumnAware.column || 1 } : undefined + } }]; } else { openables = [openable]; From eedf60ead02d77f3483b43a1136403358ca89f64 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 22 Apr 2022 09:21:21 +0200 Subject: [PATCH 227/245] ci - only publish log files when there was a failure --- build/azure-pipelines/darwin/product-build-darwin-test.yml | 2 +- build/azure-pipelines/linux/product-build-linux-client.yml | 2 +- build/azure-pipelines/win32/product-build-win32.yml | 2 +- 3 files 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 29e628df217..e28da6547eb 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-test.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-test.yml @@ -264,7 +264,7 @@ steps: targetPath: .build/logs displayName: "Publish Log Files" continueOnError: true - condition: succeededOrFailed() + condition: failed() - task: PublishTestResults@2 displayName: Publish Tests Results diff --git a/build/azure-pipelines/linux/product-build-linux-client.yml b/build/azure-pipelines/linux/product-build-linux-client.yml index bbb5b8dc597..b6472b5e573 100644 --- a/build/azure-pipelines/linux/product-build-linux-client.yml +++ b/build/azure-pipelines/linux/product-build-linux-client.yml @@ -323,7 +323,7 @@ steps: targetPath: .build/logs displayName: "Publish Log Files" continueOnError: true - condition: and(succeededOrFailed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) + condition: and(failed(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - task: PublishTestResults@2 displayName: Publish Tests Results diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 79ef705a07b..99b39ee2b25 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -282,7 +282,7 @@ steps: targetPath: .build\logs displayName: "Publish Log Files" continueOnError: true - condition: and(succeededOrFailed(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + condition: and(failed(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - task: PublishTestResults@2 displayName: Publish Tests Results From 90a6367e2c2cea1cd15fbf18c8302497d16a5bd8 Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 22 Apr 2022 11:51:53 +0200 Subject: [PATCH 228/245] remove mentionings of tabs proposal, fyi@lramos15 --- extensions/git/package.json | 1 - extensions/vscode-api-tests/package.json | 1 - 2 files changed, 2 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 117cc1b0761..ab271e1aeb8 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -15,7 +15,6 @@ "scmActionButton", "scmSelectedProvider", "scmValidation", - "tabs", "timeline" ], "categories": [ diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 00196d1fdec..501c623ba93 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -36,7 +36,6 @@ "scmActionButton", "scmSelectedProvider", "scmValidation", - "tabs", "taskPresentationGroup", "terminalDataWriteEvent", "terminalDimensions", From 2e2654c97f508f7232ef467c80525f219e3c5b08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Fri, 22 Apr 2022 12:31:15 +0200 Subject: [PATCH 229/245] address heuristic when stage ranges act on same line number diff (#145899) Co-authored-by: @lszomoru Fixes: #126989 --- extensions/git/src/staging.ts | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/extensions/git/src/staging.ts b/extensions/git/src/staging.ts index c2af4a575b5..36e9c6f26e1 100644 --- a/extensions/git/src/staging.ts +++ b/extensions/git/src/staging.ts @@ -109,12 +109,28 @@ export function intersectDiffWithRange(textDocument: TextDocument, diff: LineCha if (diff.modifiedEndLineNumber === 0) { return diff; } else { - return { - originalStartLineNumber: diff.originalStartLineNumber, - originalEndLineNumber: diff.originalEndLineNumber, - modifiedStartLineNumber: intersection.start.line + 1, - modifiedEndLineNumber: intersection.end.line + 1 - }; + const modifiedStartLineNumber = intersection.start.line + 1; + const modifiedEndLineNumber = intersection.end.line + 1; + + // heuristic: same number of lines on both sides, let's assume line by line + if (diff.originalEndLineNumber - diff.originalStartLineNumber === diff.modifiedEndLineNumber - diff.modifiedStartLineNumber) { + const delta = modifiedStartLineNumber - diff.modifiedStartLineNumber; + const length = modifiedEndLineNumber - modifiedStartLineNumber; + + return { + originalStartLineNumber: diff.originalStartLineNumber + delta, + originalEndLineNumber: diff.originalStartLineNumber + delta + length, + modifiedStartLineNumber, + modifiedEndLineNumber + }; + } else { + return { + originalStartLineNumber: diff.originalStartLineNumber, + originalEndLineNumber: diff.originalEndLineNumber, + modifiedStartLineNumber, + modifiedEndLineNumber + }; + } } } From 01ab50b9294a9d64f62eb9a121ba1d823666e7b3 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 22 Apr 2022 12:35:33 +0300 Subject: [PATCH 230/245] Improve pause connection (#147318) --- .../vscode-test-resolver/src/extension.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/extensions/vscode-test-resolver/src/extension.ts b/extensions/vscode-test-resolver/src/extension.ts index b74d79c0cc6..9f6fc9efc18 100644 --- a/extensions/vscode-test-resolver/src/extension.ts +++ b/extensions/vscode-test-resolver/src/extension.ts @@ -24,7 +24,13 @@ let outputChannel: vscode.OutputChannel; export function activate(context: vscode.ExtensionContext) { + let connectionPaused = false; + let connectionPausedEvent = new vscode.EventEmitter(); + function doResolve(_authority: string, progress: vscode.Progress<{ message?: string; increment?: number }>): Promise { + if (connectionPaused) { + throw vscode.RemoteAuthorityResolverError.TemporarilyNotAvailable('Not available right now'); + } const connectionToken = String(crypto.randomInt(0xffffffffff)); // eslint-disable-next-line no-async-promise-executor @@ -151,8 +157,8 @@ export function activate(context: vscode.ExtensionContext) { let remoteReady = true, localReady = true; const remoteSocket = net.createConnection({ port: serverAddr.port }); - let isDisconnected = connectionPaused; - connectionPausedEvent.event(_ => { + let isDisconnected = false; + const handleConnectionPause = () => { let newIsDisconnected = connectionPaused; if (isDisconnected !== newIsDisconnected) { outputChannel.appendLine(`Connection state: ${newIsDisconnected ? 'open' : 'paused'}`); @@ -175,7 +181,10 @@ export function activate(context: vscode.ExtensionContext) { } } } - }); + }; + + connectionPausedEvent.event(_ => handleConnectionPause()); + handleConnectionPause(); proxySocket.on('data', (data) => { remoteReady = remoteSocket.write(data); @@ -251,9 +260,6 @@ export function activate(context: vscode.ExtensionContext) { }); } - let connectionPaused = false; - let connectionPausedEvent = new vscode.EventEmitter(); - const authorityResolverDisposable = vscode.workspace.registerRemoteAuthorityResolver('test', { async getCanonicalURI(uri: vscode.Uri): Promise { return vscode.Uri.file(uri.path); From beea29de50f19f7336c93996a3b37cab21aa413b Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 22 Apr 2022 13:38:21 +0300 Subject: [PATCH 231/245] Fixes #147318: Do not convert `RemoteAuthorityResolverError` to `ErrorNoTelemetry` since it already extends from it --- src/vs/base/common/errors.ts | 4 ++++ .../remoteAuthorityResolverService.test.ts | 22 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/vs/platform/remote/test/electron-sandbox/remoteAuthorityResolverService.test.ts diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index 91417305378..4f397866ae4 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -235,6 +235,10 @@ export class ExpectedError extends Error { export class ErrorNoTelemetry extends Error { public static fromError(err: any): ErrorNoTelemetry { + if (err && err instanceof ErrorNoTelemetry) { + return err; + } + if (err && err instanceof Error) { const result = new ErrorNoTelemetry(); result.name = err.name; diff --git a/src/vs/platform/remote/test/electron-sandbox/remoteAuthorityResolverService.test.ts b/src/vs/platform/remote/test/electron-sandbox/remoteAuthorityResolverService.test.ts new file mode 100644 index 00000000000..81f17b25ed2 --- /dev/null +++ b/src/vs/platform/remote/test/electron-sandbox/remoteAuthorityResolverService.test.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { RemoteAuthorityResolverError, RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { RemoteAuthorityResolverService } from 'vs/platform/remote/electron-sandbox/remoteAuthorityResolverService'; + +suite('RemoteAuthorityResolverService', () => { + test('issue #147318: RemoteAuthorityResolverError keeps the same type', async () => { + const service = new RemoteAuthorityResolverService(); + const result = service.resolveAuthority('test+x'); + service._setResolvedAuthorityError('test+x', new RemoteAuthorityResolverError('something', RemoteAuthorityResolverErrorCode.TemporarilyNotAvailable)); + try { + await result; + assert.fail(); + } catch (err) { + assert.strictEqual(RemoteAuthorityResolverError.isTemporarilyNotAvailable(err), true); + } + }); +}); From e7bc9bb5fc05d2cf9e468c359f634ecc2cc5e8bb Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 22 Apr 2022 12:43:19 +0200 Subject: [PATCH 232/245] don't return duplicates from `vscode.extensions.allAcrossExtensionHosts`, add `ExtensionIdentifierSet` fyi @alexdima --- .../workbench/api/common/extHost.api.impl.ts | 9 ++- .../services/extensions/common/extensions.ts | 59 +++++++++++++++++++ 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 88ef2492dec..864b227de8e 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -91,7 +91,7 @@ import { ExtHostNotebookEditors } from 'vs/workbench/api/common/extHostNotebookE import { ExtHostNotebookDocuments } from 'vs/workbench/api/common/extHostNotebookDocuments'; import { ExtHostInteractive } from 'vs/workbench/api/common/extHostInteractive'; import { combinedDisposable } from 'vs/base/common/lifecycle'; -import { checkProposedApiEnabled, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; +import { checkProposedApiEnabled, ExtensionIdentifierSet, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { DebugConfigurationProviderTriggerKind } from 'vs/workbench/contrib/debug/common/debug'; import { ExtHostNotebookProxyKernels } from 'vs/workbench/api/common/extHostNotebookProxyKernels'; @@ -419,12 +419,11 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }, get allAcrossExtensionHosts(): vscode.Extension[] { checkProposedApiEnabled(extension, 'extensionsAny'); + const local = new ExtensionIdentifierSet(extensionInfo.mine.getAllExtensionDescriptions().map(desc => desc.identifier)); const result: vscode.Extension[] = []; - for (const desc of extensionInfo.mine.getAllExtensionDescriptions()) { - result.push(new Extension(extensionService, extension.identifier, desc, extensionKind, false)); - } for (const desc of extensionInfo.all.getAllExtensionDescriptions()) { - result.push(new Extension(extensionService, extension.identifier, desc, extensionKind /* TODO@alexdima THIS IS WRONG */, true)); + const isFromDifferentExtensionHost = !local.has(desc.identifier); + result.push(new Extension(extensionService, extension.identifier, desc, extensionKind /* TODO@alexdima THIS IS WRONG */, isFromDifferentExtensionHost)); } return result; }, diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 1a564e9a491..722dba8dd6b 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -283,6 +283,65 @@ export class ExtensionHostExtensions { } } +export class ExtensionIdentifierSet implements Set { + + readonly [Symbol.toStringTag]: string = 'ExtensionIdentifierSet'; + + private readonly _map = new Map(); + private readonly _toKey = ExtensionIdentifier.toKey; + + constructor(values?: Iterable) { + if (values) { + for (const value of values) { + this.add(value); + } + } + } + + get size(): number { + return this._map.size; + } + + add(value: ExtensionIdentifier): this { + this._map.set(this._toKey(value), value); + return this; + } + + clear(): void { + this._map.clear(); + } + + delete(value: ExtensionIdentifier): boolean { + return this._map.delete(this._toKey(value)); + } + + has(value: ExtensionIdentifier): boolean { + return this._map.has(this._toKey(value)); + } + + forEach(callbackfn: (value: ExtensionIdentifier, value2: ExtensionIdentifier, set: Set) => void, thisArg?: any): void { + this._map.forEach(value => callbackfn.call(thisArg, value, value, this)); + } + + *entries(): IterableIterator<[ExtensionIdentifier, ExtensionIdentifier]> { + for (let [_key, value] of this._map) { + yield [value, value]; + } + } + + keys(): IterableIterator { + return this._map.values(); + } + + values(): IterableIterator { + return this._map.values(); + } + + [Symbol.iterator](): IterableIterator { + return this._map.values(); + } +} + export function extensionIdentifiersArrayToSet(extensionIds: ExtensionIdentifier[]): Set { const result = new Set(); for (const extensionId of extensionIds) { From e30ef407d10c65ab987ff4b639d051e23cd10a9a Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 22 Apr 2022 14:37:54 +0300 Subject: [PATCH 233/245] Fixes #146412 --- .../browser/extensions.contribution.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 41515d1c2e2..ebbd930216f 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -200,9 +200,12 @@ Registry.as(ConfigurationExtensions.Configuration) } }, additionalProperties: false, - default: { - 'pub.name': false - } + default: {}, + defaultSnippets: [{ + 'body': { + 'pub.name': false + } + }] }, 'extensions.experimental.affinity': { type: 'object', @@ -214,9 +217,12 @@ Registry.as(ConfigurationExtensions.Configuration) } }, additionalProperties: false, - default: { - 'pub.name': 1 - } + default: {}, + defaultSnippets: [{ + 'body': { + 'pub.name': 1 + } + }] }, [WORKSPACE_TRUST_EXTENSION_SUPPORT]: { type: 'object', From a9dbd356a3208e907f085896b1ba569b8025a2eb Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 22 Apr 2022 14:18:52 +0200 Subject: [PATCH 234/245] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e064b6f1cd8..1e4ee5623c1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.67.0", - "distro": "12a90eb4bfd1f388f5a039a95223b178d14afd6d", + "distro": "4fbdb809a2af47179ee57122d59744d478683b5c", "author": { "name": "Microsoft Corporation" }, From bf58654958c3632475794f487531476d97e26de4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Fri, 22 Apr 2022 14:41:47 +0200 Subject: [PATCH 235/245] fixes #147198 --- src/vs/workbench/contrib/scm/browser/scmViewPane.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 79cce93a380..f1444d0fd6b 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -2381,6 +2381,10 @@ export class SCMViewPane extends ViewPane { override shouldShowWelcome(): boolean { return this.scmService.repositories.length === 0; } + + override getActionsContext(): unknown { + return this.scmViewService.visibleRepositories.length === 1 ? this.scmViewService.visibleRepositories[0].provider : undefined; + } } export const scmProviderSeparatorBorderColor = registerColor('scm.providerBorder', { dark: '#454545', light: '#C8C8C8', hcDark: contrastBorder, hcLight: contrastBorder }, localize('scm.providerBorder', "SCM Provider separator border.")); From b6dedccf52e9d27f31e5f46c3ecb9c694fbe350d Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 22 Apr 2022 15:14:46 +0200 Subject: [PATCH 236/245] SCM - surface sorting options in the menus (#147825) --- .../contrib/scm/browser/scmViewPane.ts | 55 ++++++++++++++++++- .../contrib/scm/browser/scmViewService.ts | 53 +++++++++++------- src/vs/workbench/contrib/scm/common/scm.ts | 8 +++ 3 files changed, 93 insertions(+), 23 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index f1444d0fd6b..cadb081fece 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -10,7 +10,7 @@ import { IDisposable, Disposable, DisposableStore, combinedDisposable, dispose, import { ViewPane, IViewPaneOptions, ViewAction } from 'vs/workbench/browser/parts/views/viewPane'; import { append, $, Dimension, asCSSUrl, trackFocus, clearNode } from 'vs/base/browser/dom'; import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; -import { ISCMResourceGroup, ISCMResource, InputValidationType, ISCMRepository, ISCMInput, IInputValidation, ISCMViewService, ISCMViewVisibleRepositoryChangeEvent, ISCMService, SCMInputChangeReason, VIEW_PANE_ID, ISCMActionButton, ISCMActionButtonDescriptor } from 'vs/workbench/contrib/scm/common/scm'; +import { ISCMResourceGroup, ISCMResource, InputValidationType, ISCMRepository, ISCMInput, IInputValidation, ISCMViewService, ISCMViewVisibleRepositoryChangeEvent, ISCMService, SCMInputChangeReason, VIEW_PANE_ID, ISCMActionButton, ISCMActionButtonDescriptor, ISCMRepositorySortKey, REPOSITORIES_VIEW_PANE_ID } from 'vs/workbench/contrib/scm/common/scm'; import { ResourceLabels, IResourceLabel, IFileLabelOptions } from 'vs/workbench/browser/labels'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -84,6 +84,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { MarkdownRenderer } from 'vs/editor/contrib/markdownRenderer/browser/markdownRenderer'; import { Button, ButtonWithDescription } from 'vs/base/browser/ui/button/button'; import { INotificationService } from 'vs/platform/notification/common/notification'; +import { RepositoryContextKeys } from 'vs/workbench/contrib/scm/browser/scmViewService'; type TreeElement = ISCMRepository | ISCMInput | ISCMActionButton | ISCMResourceGroup | IResourceNode | ISCMResource; @@ -944,7 +945,7 @@ class RepositoryVisibilityAction extends Action2 { f1: false, precondition: ContextKeyExpr.or(ContextKeys.RepositoryVisibilityCount.notEqualsTo(1), ContextKeys.RepositoryVisibility(repository).isEqualTo(false)), toggled: ContextKeys.RepositoryVisibility(repository).isEqualTo(true), - menu: { id: Menus.Repositories } + menu: { id: Menus.Repositories, group: '0_repositories' } }); this.repository = repository; } @@ -1503,6 +1504,56 @@ registerAction2(SetTreeViewModeAction); registerAction2(SetListViewModeNavigationAction); registerAction2(SetTreeViewModeNavigationAction); +abstract class RepositorySortAction extends ViewAction { + constructor(private sortKey: ISCMRepositorySortKey, title: string) { + super({ + id: `workbench.scm.action.repositories.setSortKey.${sortKey}`, + title, + viewId: VIEW_PANE_ID, + f1: false, + toggled: RepositoryContextKeys.RepositorySortKey.isEqualTo(sortKey), + menu: [ + { + id: Menus.Repositories, + group: '1_sort' + }, + { + id: MenuId.ViewTitle, + when: ContextKeyExpr.equals('view', REPOSITORIES_VIEW_PANE_ID), + group: '1_sort', + }, + ] + }); + } + + runInView(accessor: ServicesAccessor) { + accessor.get(ISCMViewService).toggleSortKey(this.sortKey); + } +} + + +class RepositorySortByDiscoveryTimeAction extends RepositorySortAction { + constructor() { + super(ISCMRepositorySortKey.DiscoveryTime, localize('repositorySortByDiscoveryTime', "Sort by Discovery Time")); + } +} + +class RepositorySortByNameAction extends RepositorySortAction { + constructor() { + super(ISCMRepositorySortKey.Name, localize('repositorySortByName', "Sort by Name")); + } +} + +class RepositorySortByPathAction extends RepositorySortAction { + constructor() { + super(ISCMRepositorySortKey.Path, localize('repositorySortByPath', "Sort by Path")); + } +} + +registerAction2(RepositorySortByDiscoveryTimeAction); +registerAction2(RepositorySortByNameAction); +registerAction2(RepositorySortByPathAction); + abstract class SetSortKeyAction extends ViewAction { constructor(private sortKey: ViewModelSortKey, title: string) { super({ diff --git a/src/vs/workbench/contrib/scm/browser/scmViewService.ts b/src/vs/workbench/contrib/scm/browser/scmViewService.ts index caba59981f3..be0176c4919 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewService.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewService.ts @@ -5,7 +5,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; -import { ISCMViewService, ISCMRepository, ISCMService, ISCMViewVisibleRepositoryChangeEvent, ISCMMenus, ISCMProvider } from 'vs/workbench/contrib/scm/common/scm'; +import { ISCMViewService, ISCMRepository, ISCMService, ISCMViewVisibleRepositoryChangeEvent, ISCMMenus, ISCMProvider, ISCMRepositorySortKey } from 'vs/workbench/contrib/scm/common/scm'; import { Iterable } from 'vs/base/common/iterator'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { SCMMenus } from 'vs/workbench/contrib/scm/browser/menus'; @@ -16,6 +16,7 @@ import { compareFileNames, comparePaths } from 'vs/base/common/comparers'; import { basename } from 'vs/base/common/resources'; import { binarySearch } from 'vs/base/common/arrays'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; function getProviderStorageKey(provider: ISCMProvider): string { return `${provider.contextValue}:${provider.label}${provider.rootUri ? `:${provider.rootUri.toString()}` : ''}`; @@ -30,7 +31,9 @@ function getRepositoryName(workspaceContextService: IWorkspaceContextService, re return folder?.uri.toString() === repository.provider.rootUri.toString() ? folder.name : basename(repository.provider.rootUri); } -type ISCMRepositoryViewSortKey = 'discovery time' | 'name' | 'path'; +export const RepositoryContextKeys = { + RepositorySortKey: new RawContextKey('scmRepositorySortKey', ISCMRepositorySortKey.DiscoveryTime), +}; interface ISCMRepositoryView { readonly repository: ISCMRepository; @@ -41,7 +44,7 @@ interface ISCMRepositoryView { export interface ISCMViewServiceState { readonly all: string[]; - readonly sortKey: ISCMRepositoryViewSortKey; + readonly sortKey: ISCMRepositorySortKey; readonly visible: number[]; } @@ -65,7 +68,7 @@ export class SCMViewService implements ISCMViewService { get visibleRepositories(): ISCMRepository[] { // In order to match the legacy behaviour, when the repositories are sorted by discovery time, // the visible repositories are sorted by the selection index instead of the discovery time. - if (this._repositoriesSortKey === 'discovery time') { + if (this._repositoriesSortKey === ISCMRepositorySortKey.DiscoveryTime) { return this._repositories.filter(r => r.selectionIndex !== -1) .sort((r1, r2) => r1.selectionIndex - r2.selectionIndex) .map(r => r.repository); @@ -150,10 +153,12 @@ export class SCMViewService implements ISCMViewService { private _onDidFocusRepository = new Emitter(); readonly onDidFocusRepository = this._onDidFocusRepository.event; - private _repositoriesSortKey: ISCMRepositoryViewSortKey; + private _repositoriesSortKey: ISCMRepositorySortKey; + private _sortKeyContextKey: IContextKey; constructor( @ISCMService scmService: ISCMService, + @IContextKeyService contextKeyService: IContextKeyService, @IInstantiationService instantiationService: IInstantiationService, @IConfigurationService private readonly configurationService: IConfigurationService, @IStorageService private readonly storageService: IStorageService, @@ -168,19 +173,12 @@ export class SCMViewService implements ISCMViewService { } this._repositoriesSortKey = this.previousState?.sortKey ?? this.getViewSortOrder(); + this._sortKeyContextKey = RepositoryContextKeys.RepositorySortKey.bindTo(contextKeyService); + this._sortKeyContextKey.set(this._repositoriesSortKey); scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables); scmService.onDidRemoveRepository(this.onDidRemoveRepository, this, this.disposables); - configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('scm.repositories.sortOrder')) { - this._repositoriesSortKey = this.getViewSortOrder(); - this._repositories.sort(this.compareRepositories.bind(this)); - - this._onDidChangeRepositories.fire({ added: Iterable.empty(), removed: Iterable.empty() }); - } - }); - for (const repository of scmService.repositories) { this.onDidAddRepository(repository); } @@ -303,6 +301,14 @@ export class SCMViewService implements ISCMViewService { } } + toggleSortKey(sortKey: ISCMRepositorySortKey): void { + this._repositoriesSortKey = sortKey; + this._sortKeyContextKey.set(this._repositoriesSortKey); + this._repositories.sort(this.compareRepositories.bind(this)); + + this._onDidChangeRepositories.fire({ added: Iterable.empty(), removed: Iterable.empty() }); + } + focus(repository: ISCMRepository | undefined): void { if (repository && !this.isVisible(repository)) { return; @@ -317,7 +323,7 @@ export class SCMViewService implements ISCMViewService { private compareRepositories(op1: ISCMRepositoryView, op2: ISCMRepositoryView): number { // Sort by discovery time - if (this._repositoriesSortKey === 'discovery time') { + if (this._repositoriesSortKey === ISCMRepositorySortKey.DiscoveryTime) { return op1.discoveryTime - op2.discoveryTime; } @@ -343,13 +349,18 @@ export class SCMViewService implements ISCMViewService { Math.max(...this._repositories.map(r => r.selectionIndex)); } - private getViewSortOrder(): ISCMRepositoryViewSortKey { - const sortOder = this.configurationService.getValue('scm.repositories.sortOrder'); - if (sortOder !== 'discovery time' && sortOder !== 'name' && sortOder !== 'path') { - return 'discovery time'; + private getViewSortOrder(): ISCMRepositorySortKey { + const sortOder = this.configurationService.getValue<'discovery time' | 'name' | 'path'>('scm.repositories.sortOrder'); + switch (sortOder) { + case 'discovery time': + return ISCMRepositorySortKey.DiscoveryTime; + case 'name': + return ISCMRepositorySortKey.Name; + case 'path': + return ISCMRepositorySortKey.Path; + default: + return ISCMRepositorySortKey.DiscoveryTime; } - - return sortOder; } private insertRepositoryView(repositories: ISCMRepositoryView[], repositoryView: ISCMRepositoryView): void { diff --git a/src/vs/workbench/contrib/scm/common/scm.ts b/src/vs/workbench/contrib/scm/common/scm.ts index c46af5c54b5..a70726447a1 100644 --- a/src/vs/workbench/contrib/scm/common/scm.ts +++ b/src/vs/workbench/contrib/scm/common/scm.ts @@ -168,6 +168,12 @@ export interface ISCMMenus { getRepositoryMenus(provider: ISCMProvider): ISCMRepositoryMenus; } +export const enum ISCMRepositorySortKey { + DiscoveryTime = 'discoveryTime', + Name = 'name', + Path = 'path' +} + export const ISCMViewService = createDecorator('scmView'); export interface ISCMViewVisibleRepositoryChangeEvent { @@ -189,6 +195,8 @@ export interface ISCMViewService { isVisible(repository: ISCMRepository): boolean; toggleVisibility(repository: ISCMRepository, visible?: boolean): void; + toggleSortKey(sortKey: ISCMRepositorySortKey): void; + readonly focusedRepository: ISCMRepository | undefined; readonly onDidFocusRepository: Event; focus(repository: ISCMRepository): void; From 93c682fe068bc5517e2a04b7ef9847f39d917f30 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 22 Apr 2022 15:16:17 +0200 Subject: [PATCH 237/245] Polish changes sorting (#147849) --- .../contrib/scm/browser/scm.contribution.ts | 2 +- .../contrib/scm/browser/scmViewPane.ts | 129 ++++++++++-------- 2 files changed, 76 insertions(+), 55 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index 75866cdf644..89b85c7ecae 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -236,7 +236,7 @@ Registry.as(ConfigurationExtensions.Configuration).regis localize('scm.defaultViewSortKey.path', "Sort the repository changes by path."), localize('scm.defaultViewSortKey.status', "Sort the repository changes by Source Control status.") ], - description: localize('scm.defaultViewSortKey', "Controls the default Source Control repository sort mode."), + description: localize('scm.defaultViewSortKey', "Controls the default Source Control repository changes sort order when viewed as a list."), default: 'path' }, 'scm.autoReveal': { diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index cadb081fece..3eb672f06c5 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -1070,9 +1070,14 @@ class ViewModel { } } + // Update sort key based on view mode + this.sortKey = this.getViewModelSortKey(); + this.refresh(); this._onDidChangeMode.fire(mode); this.modeContextKey.set(mode); + + this.storageService.store(`scm.viewMode`, mode, StorageScope.WORKSPACE, StorageTarget.USER); } get sortKey(): ViewModelSortKey { return this._sortKey; } @@ -1086,6 +1091,10 @@ class ViewModel { this.refresh(); this._onDidChangeSortKey.fire(sortKey); this.sortKeyContextKey.set(sortKey); + + if (this._mode === ViewModelMode.List) { + this.storageService.store(`scm.viewSortKey`, sortKey, StorageScope.WORKSPACE, StorageTarget.USER); + } } private _treeViewStateIsStale = false; @@ -1114,23 +1123,37 @@ class ViewModel { private scmProviderRootUriContextKey: IContextKey; private scmProviderHasRootUriContextKey: IContextKey; + private _mode: ViewModelMode; + private _sortKey: ViewModelSortKey; + private _treeViewState: ITreeViewState | undefined; + constructor( private tree: WorkbenchCompressibleObjectTree, private inputRenderer: InputRenderer, - private _mode: ViewModelMode, - private _sortKey: ViewModelSortKey, - private _treeViewState: ITreeViewState | undefined, @IInstantiationService protected instantiationService: IInstantiationService, @IEditorService protected editorService: IEditorService, @IConfigurationService protected configurationService: IConfigurationService, @ISCMViewService private scmViewService: ISCMViewService, + @IStorageService private storageService: IStorageService, @IUriIdentityService private uriIdentityService: IUriIdentityService, @IContextKeyService contextKeyService: IContextKeyService ) { + // View mode and sort key + this._mode = this.getViewModelMode(); + this._sortKey = this.getViewModelSortKey(); + + // TreeView state + const storageViewState = this.storageService.get(`scm.viewState`, StorageScope.WORKSPACE); + if (storageViewState) { + try { + this._treeViewState = JSON.parse(storageViewState); + } catch {/* noop */ } + } + this.modeContextKey = ContextKeys.ViewModelMode.bindTo(contextKeyService); - this.modeContextKey.set(_mode); + this.modeContextKey.set(this._mode); this.sortKeyContextKey = ContextKeys.ViewModelSortKey.bindTo(contextKeyService); - this.sortKeyContextKey.set(_sortKey); + this.sortKeyContextKey.set(this._sortKey); this.areAllRepositoriesCollapsedContextKey = ContextKeys.ViewModelAreAllRepositoriesCollapsed.bindTo(contextKeyService); this.isAnyRepositoryCollapsibleContextKey = ContextKeys.ViewModelIsAnyRepositoryCollapsible.bindTo(contextKeyService); this.scmProviderContextKey = ContextKeys.SCMProvider.bindTo(contextKeyService); @@ -1144,6 +1167,12 @@ class ViewModel { (this.updateRepositoryCollapseAllContextKeys, this, this.disposables); this.disposables.add(this.tree.onDidChangeCollapseState(() => this._treeViewStateIsStale = true)); + + this.storageService.onWillSaveState(e => { + if (e.reason === WillSaveStateReason.SHUTDOWN) { + this.storageService.store(`scm.viewState`, JSON.stringify(this.treeViewState), StorageScope.WORKSPACE, StorageTarget.MACHINE); + } + }); } private onDidChangeConfiguration(e?: IConfigurationChangeEvent): void { @@ -1433,6 +1462,45 @@ class ViewModel { } } + private getViewModelMode(): ViewModelMode { + let mode = this.configurationService.getValue<'tree' | 'list'>('scm.defaultViewMode') === 'list' ? ViewModelMode.List : ViewModelMode.Tree; + const storageMode = this.storageService.get(`scm.viewMode`, StorageScope.WORKSPACE) as ViewModelMode; + if (typeof storageMode === 'string') { + mode = storageMode; + } + + return mode; + } + + private getViewModelSortKey(): ViewModelSortKey { + // Tree + if (this._mode === ViewModelMode.Tree) { + return ViewModelSortKey.Path; + } + + // List + let viewSortKey: ViewModelSortKey; + const viewSortKeyString = this.configurationService.getValue<'path' | 'name' | 'status'>('scm.defaultViewSortKey'); + switch (viewSortKeyString) { + case 'name': + viewSortKey = ViewModelSortKey.Name; + break; + case 'status': + viewSortKey = ViewModelSortKey.Status; + break; + default: + viewSortKey = ViewModelSortKey.Path; + break; + } + + const storageSortKey = this.storageService.get(`scm.viewSortKey`, StorageScope.WORKSPACE) as ViewModelSortKey; + if (typeof storageSortKey === 'string') { + viewSortKey = storageSortKey; + } + + return viewSortKey; + } + dispose(): void { this.visibilityDisposables.dispose(); this.disposables.dispose(); @@ -1562,6 +1630,7 @@ abstract class SetSortKeyAction extends ViewAction { viewId: VIEW_PANE_ID, f1: false, toggled: ContextKeys.ViewModelSortKey.isEqualTo(sortKey), + precondition: ContextKeys.ViewModelMode.isEqualTo(ViewModelMode.List), menu: { id: Menus.ViewSort, group: '2_sort' } }); } @@ -2109,7 +2178,6 @@ export class SCMViewPane extends ViewPane { @IConfigurationService configurationService: IConfigurationService, @IContextKeyService contextKeyService: IContextKeyService, @IMenuService private menuService: IMenuService, - @IStorageService private storageService: IStorageService, @IOpenerService openerService: IOpenerService, @ITelemetryService telemetryService: ITelemetryService, ) { @@ -2196,44 +2264,9 @@ export class SCMViewPane extends ViewPane { append(this.listContainer, overflowWidgetsDomNode); - let viewMode = this.configurationService.getValue<'tree' | 'list'>('scm.defaultViewMode') === 'list' ? ViewModelMode.List : ViewModelMode.Tree; - - const storageMode = this.storageService.get(`scm.viewMode`, StorageScope.WORKSPACE) as ViewModelMode; - if (typeof storageMode === 'string') { - viewMode = storageMode; - } - - let viewSortKey: ViewModelSortKey; - const viewSortKeyString = this.configurationService.getValue<'path' | 'name' | 'status'>('scm.defaultViewSortKey'); - switch (viewSortKeyString) { - case 'name': - viewSortKey = ViewModelSortKey.Name; - break; - case 'status': - viewSortKey = ViewModelSortKey.Status; - break; - default: - viewSortKey = ViewModelSortKey.Path; - break; - } - - const storageSortKey = this.storageService.get(`scm.viewSortKey`, StorageScope.WORKSPACE) as ViewModelSortKey; - if (typeof storageSortKey === 'string') { - viewSortKey = storageSortKey; - } - - let viewState: ITreeViewState | undefined; - - const storageViewState = this.storageService.get(`scm.viewState`, StorageScope.WORKSPACE); - if (storageViewState) { - try { - viewState = JSON.parse(storageViewState); - } catch {/* noop */ } - } - this._register(this.instantiationService.createInstance(RepositoryVisibilityActionController)); - this._viewModel = this.instantiationService.createInstance(ViewModel, this.tree, this.inputRenderer, viewMode, viewSortKey, viewState); + this._viewModel = this.instantiationService.createInstance(ViewModel, this.tree, this.inputRenderer); this._register(this._viewModel); this.listContainer.classList.add('file-icon-themable-tree'); @@ -2242,18 +2275,11 @@ export class SCMViewPane extends ViewPane { this.updateIndentStyles(this.themeService.getFileIconTheme()); this._register(this.themeService.onDidFileIconThemeChange(this.updateIndentStyles, this)); this._register(this._viewModel.onDidChangeMode(this.onDidChangeMode, this)); - this._register(this._viewModel.onDidChangeSortKey(this.onDidChangeSortKey, this)); this._register(this.onDidChangeBodyVisibility(this._viewModel.setVisible, this._viewModel)); this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.alwaysShowRepositories'))(this.updateActions, this)); this.updateActions(); - - this._register(this.storageService.onWillSaveState(e => { - if (e.reason === WillSaveStateReason.SHUTDOWN) { - this.storageService.store(`scm.viewState`, JSON.stringify(this._viewModel.treeViewState), StorageScope.WORKSPACE, StorageTarget.MACHINE); - } - })); } private updateIndentStyles(theme: IFileIconTheme): void { @@ -2265,11 +2291,6 @@ export class SCMViewPane extends ViewPane { private onDidChangeMode(): void { this.updateIndentStyles(this.themeService.getFileIconTheme()); - this.storageService.store(`scm.viewMode`, this._viewModel.mode, StorageScope.WORKSPACE, StorageTarget.USER); - } - - private onDidChangeSortKey(): void { - this.storageService.store(`scm.viewSortKey`, this._viewModel.sortKey, StorageScope.WORKSPACE, StorageTarget.USER); } override layoutBody(height: number | undefined = this.layoutCache.height, width: number | undefined = this.layoutCache.width): void { From fccec519f8c9c685f8f77655bc34fb7140e87fa9 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Fri, 22 Apr 2022 09:26:57 -0400 Subject: [PATCH 238/245] Address some grammar --- src/vscode-dts/vscode.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index e10e43d3dce..8a6f060c429 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -15680,7 +15680,7 @@ declare module 'vscode' { readonly viewColumn: ViewColumn; /** - * The active {@link Tab tab} in the group. This is the tab which contents are currently + * The active {@link Tab tab} in the group. This is the tab whose contents are currently * being rendered. * * *Note* that there can be one active tab per group but there can only be one {@link TabGroups.activeTabGroup active group}. From cbe70edea77753f3795dcaf97e4011af1c098eb9 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Fri, 22 Apr 2022 09:32:56 -0400 Subject: [PATCH 239/245] Bump distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1e4ee5623c1..5c02dca7911 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.67.0", - "distro": "4fbdb809a2af47179ee57122d59744d478683b5c", + "distro": "f6b755dd10abe14873ceaec1acbf1e482a322722", "author": { "name": "Microsoft Corporation" }, From 1b8ca287a024feb1c18ce4ce6d94138822b42ccc Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Fri, 22 Apr 2022 09:50:13 -0400 Subject: [PATCH 240/245] Fixes #140787 watches proper worktree dir (#140789) --- extensions/git/src/git.ts | 22 ++++++++++++++++------ extensions/git/src/repository.ts | 8 ++++---- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index e37e2dbd269..4a7d1358c05 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -396,7 +396,7 @@ export class Git { return Versions.compare(Versions.fromString(this.version), Versions.fromString(version)); } - open(repository: string, dotGit: string): Repository { + open(repository: string, dotGit: { path: string, commonPath?: string }): Repository { return new Repository(this, repository, dotGit); } @@ -509,15 +509,25 @@ export class Git { return repoPath; } - async getRepositoryDotGit(repositoryPath: string): Promise { - const result = await this.exec(repositoryPath, ['rev-parse', '--git-dir']); - let dotGitPath = result.stdout.trim(); + async getRepositoryDotGit(repositoryPath: string): Promise<{ path: string, commonPath?: string }> { + const result = await this.exec(repositoryPath, ['rev-parse', '--git-dir', '--git-common-dir']); + let [dotGitPath, commonDotGitPath] = result.stdout.split('\n').map(r => r.trim()); if (!path.isAbsolute(dotGitPath)) { dotGitPath = path.join(repositoryPath, dotGitPath); } + dotGitPath = path.normalize(dotGitPath); - return path.normalize(dotGitPath); + if (commonDotGitPath) { + if (!path.isAbsolute(commonDotGitPath)) { + commonDotGitPath = path.join(repositoryPath, commonDotGitPath); + } + commonDotGitPath = path.normalize(commonDotGitPath); + + return { path: dotGitPath, commonPath: commonDotGitPath !== dotGitPath ? commonDotGitPath : undefined }; + } + + return { path: dotGitPath }; } async exec(cwd: string, args: string[], options: SpawnOptions = {}): Promise> { @@ -863,7 +873,7 @@ export class Repository { constructor( private _git: Git, private repositoryRoot: string, - readonly dotGit: string + readonly dotGit: { path: string, commonPath?: string } ) { } get git(): Git { diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index f762d54d47a..f9ae2a2ff73 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -541,7 +541,7 @@ class DotGitWatcher implements IFileWatcher { private repository: Repository, private outputChannel: OutputChannel ) { - const rootWatcher = watch(repository.dotGit); + const rootWatcher = watch(repository.dotGit.path); this.disposables.push(rootWatcher); // Ignore changes to the "index.lock" file, and watchman fsmonitor hook (https://git-scm.com/docs/githooks#_fsmonitor_watchman) cookie files. @@ -563,7 +563,7 @@ class DotGitWatcher implements IFileWatcher { this.transientDisposables = dispose(this.transientDisposables); const { name, remote } = this.repository.HEAD.upstream; - const upstreamPath = path.join(this.repository.dotGit, 'refs', 'remotes', remote, name); + const upstreamPath = path.join(this.repository.dotGit.commonPath ?? this.repository.dotGit.path, 'refs', 'remotes', remote, name); try { const upstreamWatcher = watch(upstreamPath); @@ -842,7 +842,7 @@ export class Repository implements Disposable { return this.repository.root; } - get dotGit(): string { + get dotGit(): { path: string, commonPath?: string } { return this.repository.dotGit; } @@ -874,7 +874,7 @@ export class Repository implements Disposable { this.disposables.push(dotGitFileWatcher); } catch (err) { if (Log.logLevel <= LogLevel.Error) { - outputChannel.appendLine(`${logTimestamp()} Failed to watch '${this.dotGit}', reverting to legacy API file watched. Some events might be lost.\n${err.stack || err}`); + outputChannel.appendLine(`${logTimestamp()} Failed to watch '${this.dotGit.path}', reverting to legacy API file watched. Some events might be lost.\n${err.stack || err}`); } onRepositoryDotGitFileChange = filterEvent(onRepositoryFileChange, uri => /\.git($|\/)/.test(uri.path)); From c2752e654fe651a28f8e87fddf6b05b345e5b11b Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 22 Apr 2022 16:03:12 +0200 Subject: [PATCH 241/245] Fix hygiene --- extensions/git/src/git.ts | 6 +++--- extensions/git/src/repository.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 4a7d1358c05..8fdab7f659b 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -396,7 +396,7 @@ export class Git { return Versions.compare(Versions.fromString(this.version), Versions.fromString(version)); } - open(repository: string, dotGit: { path: string, commonPath?: string }): Repository { + open(repository: string, dotGit: { path: string; commonPath?: string }): Repository { return new Repository(this, repository, dotGit); } @@ -509,7 +509,7 @@ export class Git { return repoPath; } - async getRepositoryDotGit(repositoryPath: string): Promise<{ path: string, commonPath?: string }> { + async getRepositoryDotGit(repositoryPath: string): Promise<{ path: string; commonPath?: string }> { const result = await this.exec(repositoryPath, ['rev-parse', '--git-dir', '--git-common-dir']); let [dotGitPath, commonDotGitPath] = result.stdout.split('\n').map(r => r.trim()); @@ -873,7 +873,7 @@ export class Repository { constructor( private _git: Git, private repositoryRoot: string, - readonly dotGit: { path: string, commonPath?: string } + readonly dotGit: { path: string; commonPath?: string } ) { } get git(): Git { diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index f9ae2a2ff73..76c30e5c1d4 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -842,7 +842,7 @@ export class Repository implements Disposable { return this.repository.root; } - get dotGit(): { path: string, commonPath?: string } { + get dotGit(): { path: string; commonPath?: string } { return this.repository.dotGit; } @@ -874,7 +874,7 @@ export class Repository implements Disposable { this.disposables.push(dotGitFileWatcher); } catch (err) { if (Log.logLevel <= LogLevel.Error) { - outputChannel.appendLine(`${logTimestamp()} Failed to watch '${this.dotGit.path}', reverting to legacy API file watched. Some events might be lost.\n${err.stack || err}`); + outputChannel.appendLine(`${logTimestamp()} Failed to watch path:'${this.dotGit.path}' or commonPath:'${this.dotGit.commonPath}', reverting to legacy API file watched. Some events might be lost.\n${err.stack || err}`); } onRepositoryDotGitFileChange = filterEvent(onRepositoryFileChange, uri => /\.git($|\/)/.test(uri.path)); From cb796c6514b31b07b6aa5384edf9c84bae36d816 Mon Sep 17 00:00:00 2001 From: Laurent Le Brun Date: Fri, 22 Apr 2022 16:16:03 +0200 Subject: [PATCH 242/245] Comments panel: use the date of the last reply (#147922) --- .../contrib/comments/browser/commentsTreeViewer.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts index 78a63890aa3..940de2d18d7 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts @@ -225,9 +225,9 @@ export class CommentNodeRenderer implements IListRenderer templateData.repliesMetadata.container.style.display = ''; templateData.repliesMetadata.count.textContent = this.getCountString(commentCount); - templateData.repliesMetadata.lastReplyDetail.textContent = nls.localize('lastReplyFrom', "Last reply from {0}", node.element.replies[node.element.replies.length - 1].comment.userName); - templateData.repliesMetadata.timestamp.setTimestamp(originalComment.comment.timestamp ? new Date(originalComment.comment.timestamp) : undefined); - + const lastComment = node.element.replies[node.element.replies.length - 1].comment; + templateData.repliesMetadata.lastReplyDetail.textContent = nls.localize('lastReplyFrom', "Last reply from {0}", lastComment.userName); + templateData.repliesMetadata.timestamp.setTimestamp(lastComment.timestamp ? new Date(lastComment.timestamp) : undefined); } private getCommentThreadWidgetStateColor(state: CommentThreadState | undefined, theme: IColorTheme): Color | undefined { From 9fed2f7eb336339038ae713b81bc5e1ef2238af1 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Fri, 22 Apr 2022 07:25:06 -0700 Subject: [PATCH 243/245] Enrich Git extension's remote source provider API (#147613) * Allow custom title in remote source picker * Include forks in query search results * Support `RemoteSource.detail` * Allow showing quickpicks in `getRemoteSources` * Allow custom placeholder and remote source icons * Add ability to customize placeholder * Register and show recently opened sources * Allow custom remote url labels * Add a separator label for remote sources * Update git-base typings * Make showing recent sources opt in * Add concept of recent remote source to `RemoteSourceProvider` concept * Recent sources should be sorted by timestamp * Pass current query to `getRemoteSources` * Fix applying query --- extensions/git-base/src/api/git-base.d.ts | 17 +++- extensions/git-base/src/remoteSource.ts | 81 +++++++++++++------ extensions/github/src/remoteSourceProvider.ts | 6 +- extensions/github/src/typings/git-base.d.ts | 17 +++- 4 files changed, 92 insertions(+), 29 deletions(-) diff --git a/extensions/git-base/src/api/git-base.d.ts b/extensions/git-base/src/api/git-base.d.ts index 70ac3b1b972..8510df6d043 100644 --- a/extensions/git-base/src/api/git-base.d.ts +++ b/extensions/git-base/src/api/git-base.d.ts @@ -31,9 +31,12 @@ export interface GitBaseExtension { export interface PickRemoteSourceOptions { readonly providerLabel?: (provider: RemoteSourceProvider) => string; - readonly urlLabel?: string; + readonly urlLabel?: string | ((url: string) => string); readonly providerName?: string; + readonly title?: string; + readonly placeholder?: string; readonly branch?: boolean; // then result is PickRemoteSourceResult + readonly showRecentSources?: boolean; } export interface PickRemoteSourceResult { @@ -44,17 +47,29 @@ export interface PickRemoteSourceResult { export interface RemoteSource { readonly name: string; readonly description?: string; + readonly detail?: string; + /** + * Codicon name + */ + readonly icon?: string; readonly url: string | string[]; } +export interface RecentRemoteSource extends RemoteSource { + readonly timestamp: number; +} + export interface RemoteSourceProvider { readonly name: string; /** * Codicon name */ readonly icon?: string; + readonly label?: string; + readonly placeholder?: string; readonly supportsQuery?: boolean; getBranches?(url: string): ProviderResult; + getRecentRemoteSources?(query?: string): ProviderResult; getRemoteSources(query?: string): ProviderResult; } diff --git a/extensions/git-base/src/remoteSource.ts b/extensions/git-base/src/remoteSource.ts index 50dec70863e..83e83ae1fa9 100644 --- a/extensions/git-base/src/remoteSource.ts +++ b/extensions/git-base/src/remoteSource.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { QuickPickItem, window, QuickPick } from 'vscode'; +import { QuickPickItem, window, QuickPick, QuickPickItemKind } from 'vscode'; import * as nls from 'vscode-nls'; import { RemoteSourceProvider, RemoteSource, PickRemoteSourceOptions, PickRemoteSourceResult } from './api/git-base'; import { Model } from './model'; @@ -24,17 +24,20 @@ async function getQuickPickResult(quickpick: QuickPick< class RemoteSourceProviderQuickPick { - private quickpick: QuickPick; + private quickpick: QuickPick | undefined; - constructor(private provider: RemoteSourceProvider) { - this.quickpick = window.createQuickPick(); - this.quickpick.ignoreFocusOut = true; + constructor(private provider: RemoteSourceProvider) { } - if (provider.supportsQuery) { - this.quickpick.placeholder = localize('type to search', "Repository name (type to search)"); - this.quickpick.onDidChangeValue(this.onDidChangeValue, this); - } else { - this.quickpick.placeholder = localize('type to filter', "Repository name"); + private ensureQuickPick() { + if (!this.quickpick) { + this.quickpick = window.createQuickPick(); + this.quickpick.ignoreFocusOut = true; + if (this.provider.supportsQuery) { + this.quickpick.placeholder = this.provider.placeholder ?? localize('type to search', "Repository name (type to search)"); + this.quickpick.onDidChangeValue(this.onDidChangeValue, this); + } else { + this.quickpick.placeholder = this.provider.placeholder ?? localize('type to filter', "Repository name"); + } } } @@ -45,35 +48,37 @@ class RemoteSourceProviderQuickPick { @throttle private async query(): Promise { - this.quickpick.busy = true; - try { - const remoteSources = await this.provider.getRemoteSources(this.quickpick.value) || []; + const remoteSources = await this.provider.getRemoteSources(this.quickpick?.value) || []; + + this.ensureQuickPick(); + this.quickpick!.show(); if (remoteSources.length === 0) { - this.quickpick.items = [{ + this.quickpick!.items = [{ label: localize('none found', "No remote repositories found."), alwaysShow: true }]; } else { - this.quickpick.items = remoteSources.map(remoteSource => ({ - label: remoteSource.name, + this.quickpick!.items = remoteSources.map(remoteSource => ({ + label: remoteSource.icon ? `$(${remoteSource.icon}) ${remoteSource.name}` : remoteSource.name, description: remoteSource.description || (typeof remoteSource.url === 'string' ? remoteSource.url : remoteSource.url[0]), + detail: remoteSource.detail, remoteSource, alwaysShow: true })); } } catch (err) { - this.quickpick.items = [{ label: localize('error', "$(error) Error: {0}", err.message), alwaysShow: true }]; + this.quickpick!.items = [{ label: localize('error', "$(error) Error: {0}", err.message), alwaysShow: true }]; console.error(err); } finally { - this.quickpick.busy = false; + this.quickpick!.busy = false; } } async pick(): Promise { - this.query(); - const result = await getQuickPickResult(this.quickpick); + await this.query(); + const result = await getQuickPickResult(this.quickpick!); return result?.remoteSource; } } @@ -83,6 +88,7 @@ export async function pickRemoteSource(model: Model, options: PickRemoteSourceOp export async function pickRemoteSource(model: Model, options: PickRemoteSourceOptions = {}): Promise { const quickpick = window.createQuickPick<(QuickPickItem & { provider?: RemoteSourceProvider; url?: string })>(); quickpick.ignoreFocusOut = true; + quickpick.title = options.title; if (options.providerName) { const provider = model.getRemoteProviders() @@ -93,24 +99,47 @@ export async function pickRemoteSource(model: Model, options: PickRemoteSourceOp } } - const providers = model.getRemoteProviders() + const remoteProviders = model.getRemoteProviders() .map(provider => ({ label: (provider.icon ? `$(${provider.icon}) ` : '') + (options.providerLabel ? options.providerLabel(provider) : provider.name), alwaysShow: true, provider })); - quickpick.placeholder = providers.length === 0 + const recentSources: (QuickPickItem & { url?: string; timestamp: number })[] = []; + if (options.showRecentSources) { + for (const { provider } of remoteProviders) { + const sources = (await provider.getRecentRemoteSources?.() ?? []).map((item) => { + return { + ...item, + label: (item.icon ? `$(${item.icon}) ` : '') + item.name, + url: typeof item.url === 'string' ? item.url : item.url[0], + }; + }); + recentSources.push(...sources); + } + } + + const items = [ + { kind: QuickPickItemKind.Separator, label: localize('remote sources', 'remote sources') }, + ...remoteProviders, + { kind: QuickPickItemKind.Separator, label: localize('recently opened', 'recently opened') }, + ...recentSources.sort((a, b) => b.timestamp - a.timestamp) + ]; + + quickpick.placeholder = options.placeholder ?? (remoteProviders.length === 0 ? localize('provide url', "Provide repository URL") - : localize('provide url or pick', "Provide repository URL or pick a repository source."); + : localize('provide url or pick', "Provide repository URL or pick a repository source.")); const updatePicks = (value?: string) => { if (value) { + const label = (typeof options.urlLabel === 'string' ? options.urlLabel : options.urlLabel?.(value)) ?? localize('url', "URL"); quickpick.items = [{ - label: options.urlLabel ?? localize('url', "URL"), + label: label, description: value, alwaysShow: true, url: value }, - ...providers]; + ...items + ]; } else { - quickpick.items = providers; + quickpick.items = items; } }; diff --git a/extensions/github/src/remoteSourceProvider.ts b/extensions/github/src/remoteSourceProvider.ts index 6e0b5fadbf7..c7e40884179 100644 --- a/extensions/github/src/remoteSourceProvider.ts +++ b/extensions/github/src/remoteSourceProvider.ts @@ -18,7 +18,9 @@ function asRemoteSource(raw: any): RemoteSource { const protocol = workspace.getConfiguration('github').get<'https' | 'ssh'>('gitProtocol'); return { name: `$(github) ${raw.full_name}`, - description: raw.description || undefined, + description: `${raw.stargazers_count > 0 ? `$(star-full) ${raw.stargazers_count}` : '' + }`, + detail: raw.description || undefined, url: protocol === 'https' ? raw.clone_url : raw.ssh_url }; } @@ -75,6 +77,8 @@ export class GithubRemoteSourceProvider implements RemoteSourceProvider { return []; } + query += ` fork:true`; + const raw = await octokit.search.repos({ q: query, sort: 'stars' }); return raw.data.items.map(asRemoteSource); } diff --git a/extensions/github/src/typings/git-base.d.ts b/extensions/github/src/typings/git-base.d.ts index 70ac3b1b972..8510df6d043 100644 --- a/extensions/github/src/typings/git-base.d.ts +++ b/extensions/github/src/typings/git-base.d.ts @@ -31,9 +31,12 @@ export interface GitBaseExtension { export interface PickRemoteSourceOptions { readonly providerLabel?: (provider: RemoteSourceProvider) => string; - readonly urlLabel?: string; + readonly urlLabel?: string | ((url: string) => string); readonly providerName?: string; + readonly title?: string; + readonly placeholder?: string; readonly branch?: boolean; // then result is PickRemoteSourceResult + readonly showRecentSources?: boolean; } export interface PickRemoteSourceResult { @@ -44,17 +47,29 @@ export interface PickRemoteSourceResult { export interface RemoteSource { readonly name: string; readonly description?: string; + readonly detail?: string; + /** + * Codicon name + */ + readonly icon?: string; readonly url: string | string[]; } +export interface RecentRemoteSource extends RemoteSource { + readonly timestamp: number; +} + export interface RemoteSourceProvider { readonly name: string; /** * Codicon name */ readonly icon?: string; + readonly label?: string; + readonly placeholder?: string; readonly supportsQuery?: boolean; getBranches?(url: string): ProviderResult; + getRecentRemoteSources?(query?: string): ProviderResult; getRemoteSources(query?: string): ProviderResult; } From e451242d56babc4357cc73e2d6ad7d3d5d5c2ca4 Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 22 Apr 2022 16:36:01 +0200 Subject: [PATCH 244/245] API todo --- src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts b/src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts index cdaf99b812a..ed4bfed744f 100644 --- a/src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts +++ b/src/vscode-dts/vscode.proposed.inlineCompletionsNew.d.ts @@ -101,6 +101,7 @@ declare module 'vscode' { * Represents a collection of {@link InlineCompletionItemNew inline completion items} to be presented * in the editor. */ + // TODO@API let keep this in `Additions` because of the insecurity about commands vs description export class InlineCompletionListNew { /** * The inline completion items. From 1206d80f567d8a45a700f3c569f8e4344c4db023 Mon Sep 17 00:00:00 2001 From: Laurent Le Brun Date: Fri, 22 Apr 2022 16:48:40 +0200 Subject: [PATCH 245/245] Comments panel: do not truncate the author name (#147924) --- src/vs/workbench/contrib/comments/browser/media/panel.css | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/media/panel.css b/src/vs/workbench/contrib/comments/browser/media/panel.css index 0775ef6a1aa..37bc9a17a45 100644 --- a/src/vs/workbench/contrib/comments/browser/media/panel.css +++ b/src/vs/workbench/contrib/comments/browser/media/panel.css @@ -49,8 +49,7 @@ .comments-panel .comments-panel-container .tree-container .comment-thread-container .comment-snippet-container .count, .comments-panel .comments-panel-container .tree-container .comment-thread-container .comment-metadata-container .user { - overflow: hidden; - text-overflow: ellipsis; + min-width: fit-content; } .comments-panel .comments-panel-container .tree-container .comment-thread-container .comment-snippet-container .text {