From e3e0351a3a8bdbf7513495881652c7c73667ad0e Mon Sep 17 00:00:00 2001 From: yiliang114 <1204183885@qq.com> Date: Thu, 30 May 2024 17:36:24 +0800 Subject: [PATCH 1/7] Fix #213885, make doShutdown asynchronous and wait for storage service flush when triggered by beforeunload --- .../services/lifecycle/browser/lifecycleService.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts b/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts index 351cad96bc73a..b34097fc1628f 100644 --- a/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts +++ b/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts @@ -103,10 +103,10 @@ export class BrowserLifecycleService extends AbstractLifecycleService { await this.storageService.flush(WillSaveStateReason.SHUTDOWN); // Handle shutdown without veto support - this.doShutdown(); + await this.doShutdown(); } - private doShutdown(vetoShutdown?: () => void): void { + private async doShutdown(vetoShutdown?: () => void): Promise { const logService = this.logService; // Optimistically trigger a UI state flush @@ -114,7 +114,7 @@ export class BrowserLifecycleService extends AbstractLifecycleService { // not guarantee that this is being executed // but if a dialog opens, we have a chance // to succeed. - this.storageService.flush(WillSaveStateReason.SHUTDOWN); + const p = this.storageService.flush(WillSaveStateReason.SHUTDOWN); let veto = false; @@ -147,6 +147,8 @@ export class BrowserLifecycleService extends AbstractLifecycleService { } }); + // Ensure UI state is persisted + await p; // Veto: handle if provided if (veto && typeof vetoShutdown === 'function') { return vetoShutdown(); From 1880505684fe35fbd39f2d663da1d4731777013a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 30 May 2024 14:52:34 +0200 Subject: [PATCH 2/7] improve zone revealing when showing above line 1 (#213904) fixes https://github.com/microsoft/vscode/issues/213822 --- .../browser/inlineChatZoneWidget.ts | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts index 2002f06191313..88c16b8070da0 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts @@ -76,7 +76,7 @@ export class InlineChatZoneWidget extends ZoneWidget { this._disposables.add(this.widget.onDidChangeHeight(() => { if (this.position) { // only relayout when visible - this._relayout(this._computeHeightInLines()); + this._relayout(this._computeHeight().linesValue); } })); this._disposables.add(this.widget); @@ -122,13 +122,13 @@ export class InlineChatZoneWidget extends ZoneWidget { return info.contentWidth - (info.glyphMarginWidth + info.decorationsWidth + (indentationWidth ?? 0)); } - private _computeHeightInLines(): number { + private _computeHeight(): { linesValue: number; pixelsValue: number } { const chatContentHeight = this.widget.contentHeight; const editorHeight = this.editor.getLayoutInfo().height; const contentHeight = Math.min(chatContentHeight, Math.max(this.widget.minHeight, editorHeight * 0.42)); const heightInLines = contentHeight / this.editor.getOption(EditorOption.lineHeight); - return heightInLines; + return { linesValue: heightInLines, pixelsValue: contentHeight }; } protected override _onWidth(_widthInPixel: number): void { @@ -145,17 +145,32 @@ export class InlineChatZoneWidget extends ZoneWidget { const marginWithoutIndentation = info.glyphMarginWidth + info.decorationsWidth + info.lineNumbersWidth; this.container.style.marginLeft = `${marginWithoutIndentation}px`; - super.show(position, this._computeHeightInLines()); + const height = this._computeHeight(); + super.show(position, height.linesValue); this._setWidgetMargins(position); this.widget.chatWidget.setVisible(true); this.widget.focus(); scrollState.restore(this.editor); - this.editor.revealRangeNearTopIfOutsideViewport(Range.fromPositions(position.delta(-1)), ScrollType.Immediate); + + if (position.lineNumber > 1) { + this.editor.revealRangeNearTopIfOutsideViewport(Range.fromPositions(position.delta(-1)), ScrollType.Immediate); + } else { + // reveal top of zone widget + const lineTop = this.editor.getTopForLineNumber(position.lineNumber); + const zoneTop = lineTop - height.pixelsValue; + const spaceBelowLine = this.editor.getScrollHeight() - this.editor.getBottomForLineNumber(position.lineNumber); + const minTop = this.editor.getScrollTop() - spaceBelowLine; + const newTop = Math.max(zoneTop, minTop); + + if (newTop < this.editor.getScrollTop()) { + this.editor.setScrollTop(newTop, ScrollType.Immediate); + } + } } override updatePositionAndHeight(position: Position): void { - super.updatePositionAndHeight(position, this._computeHeightInLines()); + super.updatePositionAndHeight(position, this._computeHeight().linesValue); this._setWidgetMargins(position); } From db640b3b523e254574c9adaeef0901b5c92f1fb9 Mon Sep 17 00:00:00 2001 From: Simon Siefke Date: Thu, 30 May 2024 14:53:31 +0200 Subject: [PATCH 3/7] fix wrong colors when editor findMatchForeground is not defined (#213686) * fix css --- src/vs/editor/contrib/find/browser/findWidget.css | 8 -------- src/vs/editor/contrib/find/browser/findWidget.ts | 10 +++++++++- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vs/editor/contrib/find/browser/findWidget.css b/src/vs/editor/contrib/find/browser/findWidget.css index d8c479cf1b77b..5d3ef3277d799 100644 --- a/src/vs/editor/contrib/find/browser/findWidget.css +++ b/src/vs/editor/contrib/find/browser/findWidget.css @@ -232,10 +232,6 @@ background-color: var(--vscode-editor-findMatchHighlightBackground); } -.monaco-editor .findMatchInline { - color: var(--vscode-editor-findMatchHighlightForeground); -} - .monaco-editor .currentFindMatch { background-color: var(--vscode-editor-findMatchBackground); border: 2px solid var(--vscode-editor-findMatchBorder); @@ -243,10 +239,6 @@ box-sizing: border-box; } -.monaco-editor .currentFindMatchInline { - color: var(--vscode-editor-findMatchForeground); -} - .monaco-editor .findScope { background-color: var(--vscode-editor-findRangeHighlightBackground); } diff --git a/src/vs/editor/contrib/find/browser/findWidget.ts b/src/vs/editor/contrib/find/browser/findWidget.ts index f536964cb8664..303a9a58aaa02 100644 --- a/src/vs/editor/contrib/find/browser/findWidget.ts +++ b/src/vs/editor/contrib/find/browser/findWidget.ts @@ -35,7 +35,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { asCssVariable, contrastBorder, editorFindMatchHighlightBorder, editorFindRangeHighlightBorder, inputActiveOptionBackground, inputActiveOptionBorder, inputActiveOptionForeground } from 'vs/platform/theme/common/colorRegistry'; +import { asCssVariable, contrastBorder, editorFindMatchForeground, editorFindMatchHighlightBorder, editorFindMatchHighlightForeground, editorFindRangeHighlightBorder, inputActiveOptionBackground, inputActiveOptionBorder, inputActiveOptionForeground } from 'vs/platform/theme/common/colorRegistry'; import { registerIcon, widgetClose } from 'vs/platform/theme/common/iconRegistry'; import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ThemeIcon } from 'vs/base/common/themables'; @@ -1409,4 +1409,12 @@ registerThemingParticipant((theme, collector) => { if (hcBorder) { collector.addRule(`.monaco-editor .find-widget { border: 1px solid ${hcBorder}; }`); } + const findMatchForeground = theme.getColor(editorFindMatchForeground); + if (findMatchForeground) { + collector.addRule(`.monaco-editor .findMatchInline { color: ${findMatchForeground}; }`); + } + const findMatchHighlightForeground = theme.getColor(editorFindMatchHighlightForeground); + if (findMatchHighlightForeground) { + collector.addRule(`.monaco-editor .currentFindMatchInline { color: ${findMatchHighlightForeground}; }`); + } }); From 60f4cec2e99c8333e60e135dc08061d23a3e4a37 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 30 May 2024 06:49:23 -0700 Subject: [PATCH 4/7] Only fire collapsible change event if actually changed. (#213910) --- src/vs/workbench/api/browser/mainThreadComments.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadComments.ts b/src/vs/workbench/api/browser/mainThreadComments.ts index d5e11a86dc1da..4eb6385cbed35 100644 --- a/src/vs/workbench/api/browser/mainThreadComments.ts +++ b/src/vs/workbench/api/browser/mainThreadComments.ts @@ -109,8 +109,10 @@ export class MainThreadCommentThread implements languages.CommentThread { } set collapsibleState(newState: languages.CommentThreadCollapsibleState | undefined) { - this._collapsibleState = newState; - this._onDidChangeCollapsibleState.fire(this._collapsibleState); + if (newState !== this._collapsibleState) { + this._collapsibleState = newState; + this._onDidChangeCollapsibleState.fire(this._collapsibleState); + } } private _initialCollapsibleState: languages.CommentThreadCollapsibleState | undefined; From fb4d81a2a4a2db1c9c227704fb9d64400015e7bb Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 30 May 2024 15:54:41 +0200 Subject: [PATCH 5/7] On request-removal undo till the point at which the request was made (#213902) * On request-removal undo till the point at which the request was made fixes https://github.com/microsoft/vscode-copilot/issues/5736 * fixed EOL sequence which (hopefully) fixes tests * give up, don't use new lines... --- .../browser/inlineChatController.ts | 17 +- .../inlineChat/browser/inlineChatSession.ts | 7 +- .../test/browser/inlineChatController.test.ts | 160 +++++++++++++++++- 3 files changed, 173 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 7ccb6a71164bb..259fde69a8404 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -439,9 +439,18 @@ export class InlineChatController implements IEditorContribution { } }); } else if (e.kind === 'removeRequest') { - // TODO@jrieken this is buggy/weird when having code changes from multiple turns because - // logically they are all the same hunks - this._strategy?.cancel(); + // TODO@jrieken there is still some work left for when a request "in the middle" + // is removed. We will undo all changes till that point but not remove those + // later request + const exchange = this._session!.exchanges.find(candidate => candidate.prompt.request.id === e.requestId); + if (exchange && this._editor.hasModel()) { + // undo till this point + const model = this._editor.getModel(); + const targetAltVersion = exchange.prompt.modelAltVersionId; + while (targetAltVersion < model.getAlternativeVersionId() && model.canUndo()) { + await model.undo(); + } + } } })); @@ -601,7 +610,7 @@ export class InlineChatController implements IEditorContribution { const input = request.message.text; this._ui.value.zone.widget.value = input; - this._session.addInput(new SessionPrompt(request)); + this._session.addInput(new SessionPrompt(request, this._editor.getModel()!.getAlternativeVersionId())); return State.SHOW_REQUEST; } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts index c4ecc2652eb44..b360c83a016d2 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts @@ -206,6 +206,10 @@ export class Session { // this._teldata.responseTypes += `${exchange.response instanceof ReplyResponse ? exchange.response.responseType : InlineChatResponseTypes.Empty}|`; } + get exchanges(): readonly SessionExchange[] { + return this._exchange; + } + get lastExchange(): SessionExchange | undefined { return this._exchange[this._exchange.length - 1]; } @@ -273,7 +277,8 @@ export class SessionPrompt { readonly value: string; constructor( - readonly request: IChatRequestModel + readonly request: IChatRequestModel, + readonly modelAltVersionId: number, ) { this.value = request.message.text; } diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts index f88d7a9904249..5742290cc5a2a 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts @@ -14,7 +14,7 @@ import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IDiffProviderFactoryService } from 'vs/editor/browser/widget/diffEditor/diffProviderFactoryService'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Range } from 'vs/editor/common/core/range'; -import { ITextModel } from 'vs/editor/common/model'; +import { EndOfLineSequence, ITextModel } from 'vs/editor/common/model'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { IModelService } from 'vs/editor/common/services/model'; import { TestDiffProviderFactoryService } from 'vs/editor/test/browser/diff/testDiffProviderFactoryService'; @@ -27,16 +27,16 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { IEditorProgressService, IProgressRunner } from 'vs/platform/progress/common/progress'; -import { IViewDescriptorService } from 'vs/workbench/common/views'; +import { IView, IViewDescriptorService } from 'vs/workbench/common/views'; import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; import { IAccessibleViewService } from 'vs/platform/accessibility/browser/accessibleView'; -import { IChatAccessibilityService, IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; +import { IChatAccessibilityService, IChatWidget, IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; import { ChatAgentLocation, ChatAgentService, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { IChatResponseViewModel } from 'vs/workbench/contrib/chat/common/chatViewModel'; import { InlineChatController, InlineChatRunOptions, State } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; import { Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; import { CTX_INLINE_CHAT_USER_DID_EDIT, EditMode, InlineChatConfigKeys } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; -import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { TestViewsService, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; import { IInlineChatSavingService } from '../../browser/inlineChatSavingService'; import { IInlineChatSessionService } from '../../browser/inlineChatSessionService'; import { InlineChatSessionServiceImpl } from '../../browser/inlineChatSessionServiceImpl'; @@ -61,6 +61,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { TestCommandService } from 'vs/editor/test/browser/editorTestServices'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; import { RerunAction } from 'vs/workbench/contrib/inlineChat/browser/inlineChatActions'; +import { CancellationToken } from 'vs/base/common/cancellation'; suite('InteractiveChatController', function () { @@ -127,10 +128,13 @@ suite('InteractiveChatController', function () { let model: ITextModel; let ctrl: TestController; let contextKeyService: MockContextKeyService; + let chatService: IChatService; let chatAgentService: IChatAgentService; let inlineChatSessionService: IInlineChatSessionService; let instaService: TestInstantiationService; + let chatWidget: IChatWidget; + setup(function () { const serviceCollection = new ServiceCollection( @@ -141,7 +145,11 @@ suite('InteractiveChatController', function () { [IHoverService, NullHoverService], [IExtensionService, new TestExtensionService()], [IContextKeyService, new MockContextKeyService()], - [IViewsService, new TestExtensionService()], + [IViewsService, new class extends TestViewsService { + override async openView(id: string, focus?: boolean | undefined): Promise { + return { widget: chatWidget ?? null } as any; + } + }()], [IWorkspaceContextService, new TestContextService()], [IChatWidgetHistoryService, new SyncDescriptor(ChatWidgetHistoryService)], [IChatWidgetService, new SyncDescriptor(ChatWidgetService)], @@ -193,12 +201,13 @@ suite('InteractiveChatController', function () { configurationService.setUserConfiguration('editor', {}); contextKeyService = instaService.get(IContextKeyService) as MockContextKeyService; - + chatService = instaService.get(IChatService); chatAgentService = instaService.get(IChatAgentService); inlineChatSessionService = store.add(instaService.get(IInlineChatSessionService)); model = store.add(instaService.get(IModelService).createModel('Hello\nWorld\nHello Again\nHello World\n', null)); + model.setEOL(EndOfLineSequence.LF); editor = store.add(instantiateTestCodeEditor(instaService, model)); store.add(chatAgentService.registerDynamicAgent({ id: 'testEditorAgent', ...agentData, }, { @@ -494,4 +503,143 @@ suite('InteractiveChatController', function () { ctrl.finishExistingSession(); await r; }); + + test('Retry undoes all changes, not just those from the request#5736', async function () { + + const text = [ + 'eins-', + 'zwei-', + 'drei-' + ]; + + store.add(chatAgentService.registerDynamicAgent({ + id: 'testEditorAgent2', + ...agentData + }, { + async invoke(request, progress, history, token) { + progress({ kind: 'textEdit', uri: model.uri, edits: [{ range: new Range(1, 1, 1, 1), text: text.shift() ?? '' }] }); + return {}; + }, + })); + + ctrl = instaService.createInstance(TestController, editor); + const rerun = new RerunAction(); + + model.setValue(''); + + // REQUEST 1 + const p = ctrl.waitFor([...TestController.INIT_SEQUENCE, State.SHOW_REQUEST, State.SHOW_RESPONSE, State.WAIT_FOR_INPUT]); + const r = ctrl.run({ message: '1', autoSend: true }); + await p; + + assert.strictEqual(model.getValue(), 'eins-'); + + // REQUEST 2 + const p2 = ctrl.waitFor([State.SHOW_REQUEST, State.SHOW_RESPONSE, State.WAIT_FOR_INPUT]); + await ctrl.acceptInput(); + await p2; + + assert.strictEqual(model.getValue(), 'zwei-eins-'); + + // REQUEST 2 - RERUN + const p3 = ctrl.waitFor([State.SHOW_REQUEST, State.SHOW_RESPONSE, State.WAIT_FOR_INPUT]); + await instaService.invokeFunction(rerun.runInlineChatCommand, ctrl, editor); + await p3; + + assert.strictEqual(model.getValue(), 'drei-eins-'); + + ctrl.finishExistingSession(); + await r; + + }); + + test('moving inline chat to another model undoes changes', async function () { + const text = [ + 'eins\n', + 'zwei\n' + ]; + + store.add(chatAgentService.registerDynamicAgent({ + id: 'testEditorAgent2', + ...agentData + }, { + async invoke(request, progress, history, token) { + progress({ kind: 'textEdit', uri: model.uri, edits: [{ range: new Range(1, 1, 1, 1), text: text.shift() ?? '' }] }); + return {}; + }, + })); + ctrl = instaService.createInstance(TestController, editor); + + // REQUEST 1 + const p = ctrl.waitFor([...TestController.INIT_SEQUENCE, State.SHOW_REQUEST, State.SHOW_RESPONSE, State.WAIT_FOR_INPUT]); + ctrl.run({ message: '1', autoSend: true }); + await p; + + + assert.strictEqual(model.getValue(), 'eins\nHello\nWorld\nHello Again\nHello World\n'); + + const targetModel = chatService.startSession(ChatAgentLocation.Editor, CancellationToken.None)!; + store.add(targetModel); + chatWidget = new class extends mock() { + override get viewModel() { + return { model: targetModel } as any; + } + override focusLastMessage() { } + }; + + const r = ctrl.joinCurrentRun(); + await ctrl.viewInChat(); + + assert.strictEqual(model.getValue(), 'Hello\nWorld\nHello Again\nHello World\n'); + await r; + }); + + test('moving inline chat to another model undoes changes (2 requests)', async function () { + const text = [ + 'eins\n', + 'zwei\n' + ]; + + store.add(chatAgentService.registerDynamicAgent({ + id: 'testEditorAgent2', + ...agentData + }, { + async invoke(request, progress, history, token) { + progress({ kind: 'textEdit', uri: model.uri, edits: [{ range: new Range(1, 1, 1, 1), text: text.shift() ?? '' }] }); + return {}; + }, + })); + ctrl = instaService.createInstance(TestController, editor); + + // REQUEST 1 + const p = ctrl.waitFor([...TestController.INIT_SEQUENCE, State.SHOW_REQUEST, State.SHOW_RESPONSE, State.WAIT_FOR_INPUT]); + ctrl.run({ message: '1', autoSend: true }); + await p; + + assert.strictEqual(model.getValue(), 'eins\nHello\nWorld\nHello Again\nHello World\n'); + + // REQUEST 2 + const p2 = ctrl.waitFor([State.SHOW_REQUEST, State.SHOW_RESPONSE, State.WAIT_FOR_INPUT]); + await ctrl.acceptInput(); + await p2; + + assert.strictEqual(model.getValue(), 'zwei\neins\nHello\nWorld\nHello Again\nHello World\n'); + + const targetModel = chatService.startSession(ChatAgentLocation.Editor, CancellationToken.None)!; + store.add(targetModel); + chatWidget = new class extends mock() { + override get viewModel() { + return { model: targetModel } as any; + } + override focusLastMessage() { } + }; + + const r = ctrl.joinCurrentRun(); + + await ctrl.viewInChat(); + + assert.strictEqual(model.getValue(), 'Hello\nWorld\nHello Again\nHello World\n'); + + await r; + }); }); From 06069bb945e3f8a765e7dcf9a6d5c43316c123e3 Mon Sep 17 00:00:00 2001 From: yiliang114 <1204183885@qq.com> Date: Thu, 30 May 2024 23:45:08 +0800 Subject: [PATCH 6/7] Fix #213885, make doShutdown asynchronous and wait for storage service flush when triggered by beforeunload --- .../services/lifecycle/browser/lifecycleService.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts b/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts index b34097fc1628f..5022447a79be4 100644 --- a/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts +++ b/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts @@ -109,12 +109,14 @@ export class BrowserLifecycleService extends AbstractLifecycleService { private async doShutdown(vetoShutdown?: () => void): Promise { const logService = this.logService; + let storageServiceVeto = true; // Optimistically trigger a UI state flush // without waiting for it. The browser does // not guarantee that this is being executed // but if a dialog opens, we have a chance // to succeed. - const p = this.storageService.flush(WillSaveStateReason.SHUTDOWN); + // If the refresh is not completed, it needs to be regarded as a veto to try to evoke the dialog. + this.storageService.flush(WillSaveStateReason.SHUTDOWN).finally(() => storageServiceVeto = false); let veto = false; @@ -147,10 +149,8 @@ export class BrowserLifecycleService extends AbstractLifecycleService { } }); - // Ensure UI state is persisted - await p; // Veto: handle if provided - if (veto && typeof vetoShutdown === 'function') { + if ((veto || storageServiceVeto) && typeof vetoShutdown === 'function') { return vetoShutdown(); } From 7a3345a553dd02f06e2acdc3e56173a36c1b65da Mon Sep 17 00:00:00 2001 From: yiliang114 <1204183885@qq.com> Date: Thu, 30 May 2024 23:48:55 +0800 Subject: [PATCH 7/7] Fix #213885, make doShutdown asynchronous and wait for storage service flush when triggered by beforeunload --- .../workbench/services/lifecycle/browser/lifecycleService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts b/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts index 5022447a79be4..3a1d55aa11d9f 100644 --- a/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts +++ b/src/vs/workbench/services/lifecycle/browser/lifecycleService.ts @@ -103,10 +103,10 @@ export class BrowserLifecycleService extends AbstractLifecycleService { await this.storageService.flush(WillSaveStateReason.SHUTDOWN); // Handle shutdown without veto support - await this.doShutdown(); + this.doShutdown(); } - private async doShutdown(vetoShutdown?: () => void): Promise { + private doShutdown(vetoShutdown?: () => void): void { const logService = this.logService; let storageServiceVeto = true;