From 669e5c578c94842a423bb72bc04c018fa71afa5e Mon Sep 17 00:00:00 2001 From: weartist Date: Thu, 1 Jun 2023 09:40:34 +0800 Subject: [PATCH 01/20] adjust to correct link --- 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 7bd6b1a2b14..17bb815f962 100644 --- a/extensions/npm/src/tasks.ts +++ b/extensions/npm/src/tasks.ts @@ -140,7 +140,7 @@ export async function getPackageManager(extensionContext: ExtensionContext, fold window.showInformationMessage(multiplePMWarning, learnMore, neverShowAgain).then(result => { switch (result) { case neverShowAgain: extensionContext.globalState.update(neverShowWarning, true); break; - case learnMore: env.openExternal(Uri.parse('https://nodejs.dev/learn/the-package-lock-json-file')); + case learnMore: env.openExternal(Uri.parse('https://docs.npmjs.com/cli/v9/configuring-npm/package-lock-json')); } }); } From b2c2ba8691a1de28ec7963411f7b0d1cecda0d2e Mon Sep 17 00:00:00 2001 From: Flo Date: Fri, 9 Jun 2023 15:18:17 +0000 Subject: [PATCH 02/20] Restore ShellIntegration for fish (#184659) Resolves #184659 --- src/vs/server/node/server.cli.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/server/node/server.cli.ts b/src/vs/server/node/server.cli.ts index 138f6dd8130..6163df61949 100644 --- a/src/vs/server/node/server.cli.ts +++ b/src/vs/server/node/server.cli.ts @@ -145,7 +145,7 @@ export async function main(desc: ProductDescription, args: string[]): Promise Date: Mon, 12 Jun 2023 17:02:34 +0200 Subject: [PATCH 03/20] fix: remove side effect from `some` callback --- .../workbench/api/common/extHostTerminalService.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 07f2364ffed..04b61d8b808 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -816,15 +816,12 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I } private _getTerminalObjectIndexById(array: T[], id: ExtHostTerminalIdentifier): number | null { - let index: number | null = null; - array.some((item, i) => { - const thisId = item._id; - if (thisId === id) { - index = i; - return true; - } - return false; + const index = array.findIndex(item => { + return item._id === id; }); + if (index === -1) { + return null; + } return index; } From 66b54e16972c2139980e26bc840940eca4a37862 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 13 Jun 2023 12:53:41 +0200 Subject: [PATCH 04/20] joh/tasteless orca (#185006) * Add ability to rerun prompt Extract `SessionPrompt` with tries counter that, add rerun message that is listened on during WAIT_FOR_INPUT, read number of attempts during MAKE_REQUEST https://github.com/microsoft/vscode-copilot/issues/247 * renames --- .../browser/inlineChat.contribution.ts | 1 + .../inlineChat/browser/inlineChatActions.ts | 22 ++++++++++ .../browser/inlineChatController.ts | 33 ++++++++++---- .../browser/inlineChatLivePreviewWidget.ts | 12 ++++- .../inlineChat/browser/inlineChatSession.ts | 44 ++++++++++++++----- .../browser/inlineChatStrategies.ts | 28 ++++++++++-- 6 files changed, 116 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts index 344670ab7ad..daef8334fe5 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts @@ -26,6 +26,7 @@ registerAction2(InlineChatActions.StartSessionAction); registerAction2(InlineChatActions.UnstashSessionAction); registerAction2(InlineChatActions.MakeRequestAction); registerAction2(InlineChatActions.StopRequestAction); +registerAction2(InlineChatActions.ReRunRequestAction); registerAction2(InlineChatActions.DiscardAction); registerAction2(InlineChatActions.DiscardToClipboardAction); registerAction2(InlineChatActions.DiscardUndoToNewFileAction); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index 918f1c7aecf..a29469d02b2 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts @@ -159,6 +159,28 @@ export class MakeRequestAction extends AbstractInlineChatAction { } } +export class ReRunRequestAction extends AbstractInlineChatAction { + + constructor() { + super({ + id: 'inlineChat.regenerate', + title: localize('rerun', 'Regenerate Response'), + icon: Codicon.refresh, + precondition: ContextKeyExpr.and(CTX_INLINE_CHAT_VISIBLE, CTX_INLINE_CHAT_EMPTY.negate(), CTX_INLINE_CHAT_LAST_RESPONSE_TYPE), + menu: { + id: MENU_INLINE_CHAT_WIDGET_STATUS, + group: '0_main', + order: 1, + } + }); + } + + override runInlineChatCommand(_accessor: ServicesAccessor, ctrl: InlineChatController): void { + ctrl.regenerate(); + } + +} + export class StopRequestAction extends AbstractInlineChatAction { constructor() { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 16e5cc607f0..6bf262af6d1 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -28,7 +28,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; -import { EditResponse, EmptyResponse, ErrorResponse, ExpansionState, IInlineChatSessionService, MarkdownResponse, Session, SessionExchange } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import { EditResponse, EmptyResponse, ErrorResponse, ExpansionState, IInlineChatSessionService, MarkdownResponse, Session, SessionExchange, SessionPrompt } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; import { EditModeStrategy, LivePreviewStrategy, LiveStrategy, PreviewStrategy } from 'vs/workbench/contrib/inlineChat/browser/inlineChatStrategies'; import { InlineChatZoneWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; import { CTX_INLINE_CHAT_HAS_ACTIVE_REQUEST, CTX_INLINE_CHAT_LAST_FEEDBACK, IInlineChatRequest, IInlineChatResponse, INLINE_CHAT_ID, EditMode, InlineChatResponseFeedbackKind, CTX_INLINE_CHAT_LAST_RESPONSE_TYPE, InlineChatResponseType, CTX_INLINE_CHAT_DID_EDIT, CTX_INLINE_CHAT_HAS_STASHED_SESSION } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; @@ -58,7 +58,8 @@ const enum Message { PAUSE_SESSION = 1 << 2, CANCEL_REQUEST = 1 << 3, CANCEL_INPUT = 1 << 4, - ACCEPT_INPUT = 1 << 5 + ACCEPT_INPUT = 1 << 5, + RERUN_INPUT = 1 << 6, } export interface InlineChatRunOptions { @@ -267,7 +268,7 @@ export class InlineChatController implements IEditorContribution { this._zone.value.widget.updateSlashCommands(this._activeSession.session.slashCommands ?? []); this._zone.value.widget.placeholder = this._getPlaceholderText(); - this._zone.value.widget.value = this._activeSession.lastInput ?? ''; + this._zone.value.widget.value = this._activeSession.lastInput?.value ?? ''; this._zone.value.widget.updateInfo(this._activeSession.session.message ?? localize('welcome.1', "AI-generated code may be incorrect")); this._zone.value.show(this._activeSession.wholeRange.value.getEndPosition()); this._zone.value.widget.preferredExpansionState = this._activeSession.lastExpansionState; @@ -357,6 +358,7 @@ export class InlineChatController implements IEditorContribution { private async [State.WAIT_FOR_INPUT](options: InlineChatRunOptions | undefined): Promise { assertType(this._activeSession); + assertType(this._strategy); this._zone.value.widget.placeholder = this._getPlaceholderText(); this._zone.value.show(this._activeSession.wholeRange.value.getEndPosition()); @@ -397,6 +399,15 @@ export class InlineChatController implements IEditorContribution { return State.PAUSE; } + if (message & Message.RERUN_INPUT && this._activeSession.lastExchange) { + const { lastExchange } = this._activeSession; + this._activeSession.addInput(lastExchange.prompt.retry()); + if (lastExchange.response instanceof EditResponse) { + await this._strategy.undoChanges(lastExchange.response); + } + return State.MAKE_REQUEST; + } + if (!this._zone.value.widget.value) { return State.WAIT_FOR_INPUT; } @@ -420,7 +431,7 @@ export class InlineChatController implements IEditorContribution { return State.WAIT_FOR_INPUT; } - this._activeSession.addInput(input); + this._activeSession.addInput(new SessionPrompt(input)); return State.MAKE_REQUEST; } @@ -444,10 +455,10 @@ export class InlineChatController implements IEditorContribution { const sw = StopWatch.create(); const request: IInlineChatRequest = { - prompt: this._activeSession.lastInput, + prompt: this._activeSession.lastInput.value, + attempt: this._activeSession.lastInput.attempt, selection: this._editor.getSelection(), wholeRange: this._activeSession.wholeRange.value, - attempt: 0, }; const task = this._activeSession.provider.provideResponse(this._activeSession.session, request, requestCts.token); this._log('request started', this._activeSession.provider.debugName, this._activeSession.session, request); @@ -463,7 +474,7 @@ export class InlineChatController implements IEditorContribution { if (reply?.type === 'message') { response = new MarkdownResponse(this._activeSession.textModelN.uri, reply); } else if (reply) { - response = new EditResponse(this._activeSession.textModelN.uri, reply); + response = new EditResponse(this._activeSession.textModelN.uri, this._activeSession.textModelN.getAlternativeVersionId(), reply); } else { response = new EmptyResponse(); } @@ -483,7 +494,7 @@ export class InlineChatController implements IEditorContribution { msgListener.dispose(); typeListener.dispose(); - this._activeSession.addExchange(new SessionExchange(request.prompt, response)); + this._activeSession.addExchange(new SessionExchange(this._activeSession.lastInput, response)); if (message & Message.CANCEL_SESSION) { return State.CANCEL; @@ -646,6 +657,10 @@ export class InlineChatController implements IEditorContribution { this._messages.fire(Message.ACCEPT_INPUT); } + regenerate(): void { + this._messages.fire(Message.RERUN_INPUT); + } + cancelCurrentRequest(): void { this._messages.fire(Message.CANCEL_INPUT | Message.CANCEL_REQUEST); } @@ -683,7 +698,7 @@ export class InlineChatController implements IEditorContribution { viewInChat() { if (this._activeSession?.lastExchange?.response instanceof MarkdownResponse) { - this._instaService.invokeFunction(showMessageResponse, this._activeSession.lastExchange.prompt, this._activeSession.lastExchange.response.raw.message.value); + this._instaService.invokeFunction(showMessageResponse, this._activeSession.lastExchange.prompt.value, this._activeSession.lastExchange.response.raw.message.value); } } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts index 121063c0f3d..4fedbe31063 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts @@ -44,6 +44,7 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { private readonly _inlineDiffDecorations: IEditorDecorationsCollection; private _dim: Dimension | undefined; private _isVisible: boolean = false; + private _isDiffLocked: boolean = false; constructor( editor: ICodeEditor, @@ -134,6 +135,8 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { override show(): void { assertType(this.editor.hasModel()); this._sessionStore.clear(); + this._isDiffLocked = false; + this._isVisible = true; this._sessionStore.add(this._diffEditor.onDidUpdateDiff(() => { const result = this._diffEditor.getDiffComputationResult(); @@ -148,12 +151,19 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { } })); this._updateFromChanges(this._session.wholeRange.value, this._session.lastTextModelChanges); - this._isVisible = true; + } + + lockToDiff(): void { + this._isDiffLocked = true; } private _updateFromChanges(range: Range, changes: readonly LineRangeMapping[]): void { assertType(this.editor.hasModel()); + if (this._isDiffLocked) { + return; + } + if (changes.length === 0 || this._session.textModel0.getValueLength() === 0) { // no change or changes to an empty file this._logService.debug('[IE] livePreview-mode: no diff'); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts index fdf9dd3eb48..141b5911cff 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts @@ -108,7 +108,7 @@ class SessionWholeRange { export class Session { - private _lastInput: string | undefined; + private _lastInput: SessionPrompt | undefined; private _lastExpansionState: ExpansionState | undefined; private _lastTextModelChanges: readonly LineRangeMapping[] | undefined; private _isUnstashed: boolean = false; @@ -139,10 +139,14 @@ export class Session { }; } - addInput(input: string): void { + addInput(input: SessionPrompt): void { this._lastInput = input; } + get lastInput() { + return this._lastInput; + } + get isUnstashed(): boolean { return this._isUnstashed; } @@ -151,10 +155,6 @@ export class Session { this._isUnstashed = true; } - get lastInput() { - return this._lastInput; - } - get lastExpansionState(): ExpansionState | undefined { return this._lastExpansionState; } @@ -229,7 +229,7 @@ export class Session { for (const exchange of this._exchange) { const response = exchange.response; if (response instanceof MarkdownResponse || response instanceof EditResponse) { - result.exchanges.push({ prompt: exchange.prompt, res: response.raw }); + result.exchanges.push({ prompt: exchange.prompt.value, res: response.raw }); } } return result; @@ -237,9 +237,29 @@ export class Session { } -export class SessionExchange { +export class SessionPrompt { + + private _attempt: number = 0; + constructor( - readonly prompt: string, + readonly value: string, + ) { } + + get attempt() { + return this._attempt; + } + + retry() { + const result = new SessionPrompt(this.value); + result._attempt = this._attempt + 1; + return result; + } +} + +export class SessionExchange { + + constructor( + readonly prompt: SessionPrompt, readonly response: MarkdownResponse | EditResponse | EmptyResponse | ErrorResponse ) { } } @@ -275,7 +295,11 @@ export class EditResponse { readonly workspaceEdits: ResourceEdit[] | undefined; readonly workspaceEditsIncludeLocalEdits: boolean = false; - constructor(localUri: URI, readonly raw: IInlineChatBulkEditResponse | IInlineChatEditResponse) { + constructor( + localUri: URI, + readonly modelAltVersionId: number, + readonly raw: IInlineChatBulkEditResponse | IInlineChatEditResponse + ) { if (raw.type === 'editorEdit') { // this.localEdits = raw.edits; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts index e8a718844da..03bb8f3e7ea 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts @@ -13,7 +13,7 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { IEditorDecorationsCollection } from 'vs/editor/common/editorCommon'; -import { ICursorStateComputer, IModelDecorationOptions, IModelDeltaDecoration, IValidEditOperation } from 'vs/editor/common/model'; +import { ICursorStateComputer, IModelDecorationOptions, IModelDeltaDecoration, ITextModel, IValidEditOperation } from 'vs/editor/common/model'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { localize } from 'vs/nls'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -37,6 +37,8 @@ export abstract class EditModeStrategy { abstract makeChanges(edits: ISingleEditOperation[]): Promise; + abstract undoChanges(response: EditResponse): Promise; + abstract renderChanges(response: EditResponse): Promise; abstract toggleDiff(): void; @@ -110,6 +112,10 @@ export class PreviewStrategy extends EditModeStrategy { // nothing to do } + override async undoChanges(_response: EditResponse): Promise { + // nothing to do + } + override async renderChanges(response: EditResponse): Promise { if (response.localEdits.length > 0) { const edits = response.localEdits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text)); @@ -275,9 +281,7 @@ export class LiveStrategy extends EditModeStrategy { return; } const targetAltVersion = textModelNSnapshotAltVersion ?? textModelNAltVersion; - while (targetAltVersion < modelN.getAlternativeVersionId() && modelN.canUndo()) { - modelN.undo(); - } + LiveStrategy._undoModelUntil(modelN, targetAltVersion); } override async makeChanges(edits: ISingleEditOperation[], ignoreInlineDiff?: boolean): Promise { @@ -297,6 +301,11 @@ export class LiveStrategy extends EditModeStrategy { this._editor.executeEdits('inline-chat-live', edits, ignoreInlineDiff ? undefined : cursorStateComputerAndInlineDiffCollection); } + override async undoChanges(response: EditResponse): Promise { + const { textModelN } = this._session; + LiveStrategy._undoModelUntil(textModelN, response.modelAltVersionId); + } + override async renderChanges(response: EditResponse) { this._inlineDiffDecorations.update(); @@ -309,6 +318,12 @@ export class LiveStrategy extends EditModeStrategy { } } + private static _undoModelUntil(model: ITextModel, targetAltVersion: number): void { + while (targetAltVersion < model.getAlternativeVersionId() && model.canUndo()) { + model.undo(); + } + } + protected _updateSummaryMessage() { let linesChanged = 0; for (const change of this._session.lastTextModelChanges) { @@ -373,6 +388,11 @@ export class LivePreviewStrategy extends LiveStrategy { } } + override async undoChanges(response: EditResponse): Promise { + this._diffZone.lockToDiff(); + super.undoChanges(response); + } + protected override _doToggleDiff(): void { const scrollState = StableEditorScrollState.capture(this._editor); if (this._diffEnabled) { From ceb15e0fb7d81a9630598a9d0d5b7ac9b5955808 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 13 Jun 2023 15:13:58 +0200 Subject: [PATCH 05/20] Keyboard: the configure button in the notification pop is not accessible by tab key (fix #184711) (#185013) --- .../browser/parts/notifications/media/notificationsList.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/notifications/media/notificationsList.css b/src/vs/workbench/browser/parts/notifications/media/notificationsList.css index ddb96275cd2..475cc3a6f19 100644 --- a/src/vs/workbench/browser/parts/notifications/media/notificationsList.css +++ b/src/vs/workbench/browser/parts/notifications/media/notificationsList.css @@ -87,9 +87,9 @@ height: 22px; } +.monaco-workbench .notifications-list-container .monaco-list:focus-within .notification-list-item .notification-list-item-toolbar-container, .monaco-workbench .notifications-list-container .notification-list-item:hover .notification-list-item-toolbar-container, -.monaco-workbench .notifications-list-container .monaco-list-row.focused .notification-list-item .notification-list-item-toolbar-container, -.monaco-workbench .notifications-list-container .notification-list-item.expanded .notification-list-item-toolbar-container { +.monaco-workbench .notifications-list-container .monaco-list-row.focused .notification-list-item .notification-list-item-toolbar-container { display: block; } From 00b14b2c62db7074e97c1ba77775df4779c889aa Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 13 Jun 2023 16:19:30 +0200 Subject: [PATCH 06/20] update list of repos (#185019) --- .vscode/notebooks/my-work.github-issues | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index bf86a804dff..99932de7f26 100644 --- a/.vscode/notebooks/my-work.github-issues +++ b/.vscode/notebooks/my-work.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-markdown-languageservice\n\n// current milestone name\n$milestone=milestone:\"June 2023\"" + "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release\n\n// current milestone name\n$milestone=milestone:\"June 2023\"" }, { "kind": 1, From 9f70132f21603bc2e3dc8f47ec661c28715b375b Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Tue, 13 Jun 2023 09:08:49 -0700 Subject: [PATCH 07/20] Fix storing cloud changes with deleted file (#185037) --- .../browser/editSessions.contribution.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts b/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts index 84c1bd8f1d7..33850a1817c 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts @@ -707,14 +707,15 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo hasEdits = true; - const contents = encodeBase64((await this.fileService.readFile(uri)).value); - editSessionSize += contents.length; - if (editSessionSize > this.editSessionsStorageService.SIZE_LIMIT) { - this.notificationService.error(localize('payload too large', 'Your working changes exceed the size limit and cannot be stored.')); - return undefined; - } if (await this.fileService.exists(uri)) { + const contents = encodeBase64((await this.fileService.readFile(uri)).value); + editSessionSize += contents.length; + if (editSessionSize > this.editSessionsStorageService.SIZE_LIMIT) { + this.notificationService.error(localize('payload too large', 'Your working changes exceed the size limit and cannot be stored.')); + return undefined; + } + workingChanges.push({ type: ChangeType.Addition, fileType: FileType.File, contents: contents, relativeFilePath: relativeFilePath }); } else { // Assume it's a deletion From d037fad5895e2a4a39611aaa299374f98cbd7e3e Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 13 Jun 2023 18:29:50 +0200 Subject: [PATCH 08/20] changes from review --- .../browser/inlineChatController.ts | 7 ++--- .../browser/inlineChatStrategies.ts | 27 ++++--------------- 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index b5c3d8f695d..0c668574ec1 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -196,14 +196,15 @@ export class InlineChatController implements IEditorContribution { private _showWidget(initialRender: boolean = false) { assertType(this._activeSession); assertType(this._strategy); + assertType(this._editor.hasModel()); - let widgetPosition: Position | null; + let widgetPosition: Position | undefined; if (initialRender) { widgetPosition = this._editor.getPosition(); } else { - widgetPosition = this._strategy.getWidgetPosition(); + widgetPosition = this._strategy.getWidgetPosition() ?? this._zone.value.position ?? this._activeSession.wholeRange.value.getEndPosition(); } - this._zone.value.show((widgetPosition ?? this._zone.value.position) ?? this._activeSession.wholeRange.value.getEndPosition()); + this._zone.value.show(widgetPosition); } protected async _nextState(state: State, options: InlineChatRunOptions | undefined): Promise { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts index 921e07e44f6..3182adaf006 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts @@ -43,7 +43,7 @@ export abstract class EditModeStrategy { abstract hasFocus(): boolean; - abstract getWidgetPosition(): Position | null; + abstract getWidgetPosition(): Position | undefined; } export class PreviewStrategy extends EditModeStrategy { @@ -131,8 +131,8 @@ export class PreviewStrategy extends EditModeStrategy { // nothing to do } - getWidgetPosition(): Position | null { - return null; + getWidgetPosition(): Position | undefined { + return; } hasFocus(): boolean { @@ -331,7 +331,7 @@ export class LiveStrategy extends EditModeStrategy { this._widget.updateStatus(message); } - private _lastLineOfLocalEdits(): number | undefined { + override getWidgetPosition(): Position | undefined { const lastTextModelChanges = this._session.lastTextModelChanges; let lastLineOfLocalEdits: number | undefined; for (const change of lastTextModelChanges) { @@ -340,16 +340,7 @@ export class LiveStrategy extends EditModeStrategy { lastLineOfLocalEdits = changeEndLineNumber; } } - return lastLineOfLocalEdits; - } - - override getWidgetPosition(): Position | null { - const isEditResponse = this._session.lastExchange?.response instanceof EditResponse; - if (isEditResponse) { - const lastLineOfLocalEdits = this._lastLineOfLocalEdits(); - return lastLineOfLocalEdits ? new Position(lastLineOfLocalEdits, 1) : null; - } - return null; + return lastLineOfLocalEdits ? new Position(lastLineOfLocalEdits, 1) : undefined; } hasFocus(): boolean { @@ -410,14 +401,6 @@ export class LivePreviewStrategy extends LiveStrategy { scrollState.restore(this._editor); } - override getWidgetPosition(): Position | null { - const isEditResponse = this._session.lastExchange?.response instanceof EditResponse; - if (isEditResponse) { - return this._session.wholeRange.value.getEndPosition(); - } - return null; - } - override hasFocus(): boolean { return super.hasFocus() || this._diffZone.hasFocus() || this._previewZone.hasFocus(); } From 4b939d0a0e4ad5d11170ba1a8cea8051f284791b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 13 Jun 2023 19:56:46 +0200 Subject: [PATCH 09/20] unc - fix path traversal bypass (#185048) --- build/gulpfile.reh.js | 2 +- build/lib/electron.js | 4 ++-- build/lib/electron.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index e3d9a477437..a930ce09492 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -165,7 +165,7 @@ function nodejs(platform, arch) { if (platform === 'win32') { if (product.nodejsRepository) { log(`Downloading node.js ${nodeVersion} ${platform} ${arch} from ${product.nodejsRepository}...`); - return assetFromGithub(product.nodejsRepository, nodeVersion, name => name === `win-${arch}-node.exe`) + return assetFromGithub(product.nodejsRepository, nodeVersion, name => name === `win-${arch}-node-patched.exe`) .pipe(rename('node.exe')); } log(`Downloading node.js ${nodeVersion} ${platform} ${arch} from https://nodejs.org`); diff --git a/build/lib/electron.js b/build/lib/electron.js index 4a51a8f254e..5cf792c491c 100644 --- a/build/lib/electron.js +++ b/build/lib/electron.js @@ -76,7 +76,7 @@ function darwinBundleDocumentTypes(types, icon) { }); } exports.config = { - version: product.electronRepository ? '22.5.5' : util.getElectronVersion(), + version: product.electronRepository ? '22.5.7' : util.getElectronVersion(), productAppName: product.nameLong, companyName: 'Microsoft Corporation', copyright: 'Copyright (C) 2023 Microsoft. All rights reserved', @@ -193,7 +193,7 @@ function getElectron(arch) { }; } async function main(arch = process.arch) { - const version = product.electronRepository ? '22.5.5' : util.getElectronVersion(); + const version = product.electronRepository ? '22.5.7' : util.getElectronVersion(); const electronPath = path.join(root, '.build', 'electron'); const versionFile = path.join(electronPath, 'version'); const isUpToDate = fs.existsSync(versionFile) && fs.readFileSync(versionFile, 'utf8') === `${version}`; diff --git a/build/lib/electron.ts b/build/lib/electron.ts index dfc74c01658..1d11429e586 100644 --- a/build/lib/electron.ts +++ b/build/lib/electron.ts @@ -91,7 +91,7 @@ function darwinBundleDocumentTypes(types: { [name: string]: string | string[] }, } export const config = { - version: product.electronRepository ? '22.5.5' : util.getElectronVersion(), + version: product.electronRepository ? '22.5.7' : util.getElectronVersion(), productAppName: product.nameLong, companyName: 'Microsoft Corporation', copyright: 'Copyright (C) 2023 Microsoft. All rights reserved', @@ -212,7 +212,7 @@ function getElectron(arch: string): () => NodeJS.ReadWriteStream { } async function main(arch = process.arch): Promise { - const version = product.electronRepository ? '22.5.5' : util.getElectronVersion(); + const version = product.electronRepository ? '22.5.7' : util.getElectronVersion(); const electronPath = path.join(root, '.build', 'electron'); const versionFile = path.join(electronPath, 'version'); const isUpToDate = fs.existsSync(versionFile) && fs.readFileSync(versionFile, 'utf8') === `${version}`; From 470b6efc0c5783a687f3ac1aa11befd89c463a57 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 13 Jun 2023 20:16:39 +0200 Subject: [PATCH 10/20] diff editor v2: sync update (#185039) --- .../diffEditorWidget2/diffEditorWidget2.ts | 2 - .../widget/diffEditorWidget2/diffModel.ts | 150 +++++++++++------- .../diffEditorWidget2/unchangedRanges.ts | 8 +- .../editor/common/diff/linesDiffComputer.ts | 16 ++ src/vs/monaco.d.ts | 4 + 5 files changed, 120 insertions(+), 60 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts index 74346212f94..a4c4cc5821e 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts @@ -306,7 +306,6 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { if (!m) { return; } const movedText = m.diff.get()!.movedTexts.find(m => m.lineRangeMapping.originalRange.contains(e.position.lineNumber)); - m.syncedMovedTexts.set(movedText, undefined); })); return editor; @@ -322,7 +321,6 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { if (!m) { return; } const movedText = m.diff.get()!.movedTexts.find(m => m.lineRangeMapping.modifiedRange.contains(e.position.lineNumber)); - m.syncedMovedTexts.set(movedText, undefined); })); // Revert change when an arrow is clicked. diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffModel.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffModel.ts index cfab19c8ed4..4ef2d808a89 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffModel.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffModel.ts @@ -7,25 +7,30 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { Disposable } from 'vs/base/common/lifecycle'; import { IObservable, IReader, ITransaction, derived, observableSignal, observableSignalFromEvent, observableValue, transaction } from 'vs/base/common/observable'; import { autorunWithStore2 } from 'vs/base/common/observableImpl/autorun'; +import { isDefined } from 'vs/base/common/types'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { Range } from 'vs/editor/common/core/range'; import { IDocumentDiff, IDocumentDiffProvider } from 'vs/editor/common/diff/documentDiffProvider'; -import { LineRangeMapping, MovedText, RangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; +import { LineRangeMapping, MovedText, RangeMapping, SimpleLineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; import { lineRangeMappingFromRangeMappings } from 'vs/editor/common/diff/standardLinesDiffComputer'; import { IDiffEditorModel } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import { TextEditInfo } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/beforeEditPositionMapper'; import { combineTextEditInfos } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/combineTextEditInfos'; -import { lengthAdd, lengthDiffNonNegative, lengthOfRange, lengthToPosition, lengthZero, positionToLength } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/length'; +import { lengthAdd, lengthDiffNonNegative, lengthGetLineCount, lengthOfRange, lengthToPosition, lengthZero, positionToLength } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/length'; export class DiffModel extends Disposable { private readonly _isDiffUpToDate = observableValue('isDiffUpToDate', false); public readonly isDiffUpToDate: IObservable = this._isDiffUpToDate; + private _lastDiff: IDocumentDiff | undefined; private readonly _diff = observableValue('diff', undefined); public readonly diff: IObservable = this._diff; - private readonly _unchangedRegions = observableValue<{ regions: UnchangedRegion[]; originalDecorationIds: string[]; modifiedDecorationIds: string[] }>('unchangedRegion', { regions: [], originalDecorationIds: [], modifiedDecorationIds: [] }); + private readonly _unchangedRegions = observableValue<{ regions: UnchangedRegion[]; originalDecorationIds: string[]; modifiedDecorationIds: string[] }>( + 'unchangedRegion', + { regions: [], originalDecorationIds: [], modifiedDecorationIds: [] } + ); public readonly unchangedRegions: IObservable = derived('unchangedRegions', r => this._hideUnchangedRegions.read(r) ? this._unchangedRegions.read(r).regions : [] ); @@ -50,11 +55,14 @@ export class DiffModel extends Disposable { if (!diff) { return; } - /*const textEdits = TextEditInfo.fromModelContentChanges(e.changes); - this._diff.set( - applyModifiedEdits(diff, textEdits, model.original, model.modified), - undefined - );*/ + if (!this._showMoves.get()) { + const textEdits = TextEditInfo.fromModelContentChanges(e.changes); + this._lastDiff = applyModifiedEdits(this._lastDiff!, textEdits, model.original, model.modified); + this._diff.set(DiffState.fromDiffResult(this._lastDiff), undefined); + const currentSyncedMovedText = this.syncedMovedTexts.get(); + this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff.moves.find(m => m.lineRangeMapping.modifiedRange.intersect(currentSyncedMovedText.lineRangeMapping.modifiedRange)) : undefined, undefined); + } + debouncer.schedule(); })); this._register(model.original.onDidChangeContent((e) => { @@ -62,11 +70,13 @@ export class DiffModel extends Disposable { if (!diff) { return; } - /*const textEdits = TextEditInfo.fromModelContentChanges(e.changes); - this._diff.set( - applyOriginalEdits(diff, textEdits, model.original, model.modified), - undefined - );*/ + if (!this._showMoves.get()) { + const textEdits = TextEditInfo.fromModelContentChanges(e.changes); + this._lastDiff = applyOriginalEdits(this._lastDiff!, textEdits, model.original, model.modified); + this._diff.set(DiffState.fromDiffResult(this._lastDiff), undefined); + const currentSyncedMovedText = this.syncedMovedTexts.get(); + this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff.moves.find(m => m.lineRangeMapping.modifiedRange.intersect(currentSyncedMovedText.lineRangeMapping.modifiedRange)) : undefined, undefined); + } debouncer.schedule(); })); @@ -137,8 +147,11 @@ export class DiffModel extends Disposable { ); transaction(tx => { + this._lastDiff = result; this._diff.set(DiffState.fromDiffResult(result), tx); this._isDiffUpToDate.set(true, tx); + const currentSyncedMovedText = this.syncedMovedTexts.get(); + this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff.moves.find(m => m.lineRangeMapping.modifiedRange.intersect(currentSyncedMovedText.lineRangeMapping.modifiedRange)) : undefined, tx); this._unchangedRegions.set( { @@ -152,7 +165,7 @@ export class DiffModel extends Disposable { })); } - public revealModifiedLine(lineNumber: number, tx: ITransaction): void { + public ensureModifiedLineIsVisible(lineNumber: number, tx: ITransaction): void { const unchangedRegions = this._unchangedRegions.get().regions; for (const r of unchangedRegions) { if (r.getHiddenModifiedRange(undefined).contains(lineNumber)) { @@ -162,7 +175,7 @@ export class DiffModel extends Disposable { } } - public revealOriginalLine(lineNumber: number, tx: ITransaction): void { + public ensureOriginalLineIsVisible(lineNumber: number, tx: ITransaction): void { const unchangedRegions = this._unchangedRegions.get().regions; for (const r of unchangedRegions) { if (r.getHiddenOriginalRange(undefined).contains(lineNumber)) { @@ -315,38 +328,17 @@ function applyOriginalEdits(diff: IDocumentDiff, textEdits: TextEditInfo[], orig return diff; } - const diffTextEdits = diff.changes.flatMap(c => c.innerChanges!.map(c => new TextEditInfo( - positionToLength(c.modifiedRange.getStartPosition()), - positionToLength(c.modifiedRange.getEndPosition()), - lengthOfRange(c.originalRange).toLength(), - ))); - - const combined = combineTextEditInfos(diffTextEdits, textEdits); - - let lastModifiedEndOffset = lengthZero; - let lastOriginalEndOffset = lengthZero; - const rangeMappings = combined.map(c => { - const originalStartOffset = lengthAdd(lastOriginalEndOffset, lengthDiffNonNegative(lastModifiedEndOffset, c.startOffset)); - lastModifiedEndOffset = c.endOffset; - lastOriginalEndOffset = lengthAdd(originalStartOffset, c.newLength); - - return new RangeMapping( - Range.fromPositions(lengthToPosition(originalStartOffset), lengthToPosition(lastOriginalEndOffset)), - Range.fromPositions(lengthToPosition(c.startOffset), lengthToPosition(c.endOffset)), - ); - }); - - const changes = lineRangeMappingFromRangeMappings( - rangeMappings, - originalTextModel.getLinesContent(), - modifiedTextModel.getLinesContent(), - ); + const diff2 = flip(diff); + const diff3 = applyModifiedEdits(diff2, textEdits, modifiedTextModel, originalTextModel); + return flip(diff3); +} +function flip(diff: IDocumentDiff): IDocumentDiff { return { - identical: false, - quitEarly: false, - changes, - moves: [], + changes: diff.changes.map(c => c.flip()), + moves: diff.moves.map(m => m.flip()), + identical: diff.identical, + quitEarly: diff.quitEarly, }; } @@ -355,7 +347,63 @@ function applyModifiedEdits(diff: IDocumentDiff, textEdits: TextEditInfo[], orig return diff; } - const diffTextEdits = diff.changes.flatMap(c => c.innerChanges!.map(c => new TextEditInfo( + const changes = applyModifiedEditsToLineRangeMappings(diff.changes, textEdits, originalTextModel, modifiedTextModel); + + const moves = diff.moves.map(m => { + const newModifiedRange = applyEditToLineRange(m.lineRangeMapping.modifiedRange, textEdits); + return newModifiedRange ? new MovedText( + new SimpleLineRangeMapping(m.lineRangeMapping.originalRange, newModifiedRange), + applyModifiedEditsToLineRangeMappings(m.changes, textEdits, originalTextModel, modifiedTextModel), + ) : undefined; + }).filter(isDefined); + + return { + identical: false, + quitEarly: false, + changes, + moves, + }; +} + +function applyEditToLineRange(range: LineRange, textEdits: TextEditInfo[]): LineRange | undefined { + let rangeStartLineNumber = range.startLineNumber; + let rangeEndLineNumberEx = range.endLineNumberExclusive; + + for (let i = textEdits.length - 1; i >= 0; i--) { + const textEdit = textEdits[i]; + const textEditStartLineNumber = lengthGetLineCount(textEdit.startOffset) + 1; + const textEditEndLineNumber = lengthGetLineCount(textEdit.endOffset) + 1; + const newLengthLineCount = lengthGetLineCount(textEdit.newLength); + const delta = newLengthLineCount - (textEditEndLineNumber - textEditStartLineNumber); + + if (textEditEndLineNumber < rangeStartLineNumber) { + // the text edit is before us + rangeStartLineNumber += delta; + rangeEndLineNumberEx += delta; + } else if (textEditStartLineNumber > rangeEndLineNumberEx) { + // the text edit is after us + // NOOP + } else if (textEditStartLineNumber < rangeStartLineNumber && rangeEndLineNumberEx < textEditEndLineNumber) { + // the range is fully contained in the text edit + return undefined; + } else if (textEditStartLineNumber < rangeStartLineNumber && textEditEndLineNumber <= rangeEndLineNumberEx) { + // the text edit ends inside our range + rangeStartLineNumber = textEditEndLineNumber + 1; + rangeStartLineNumber += delta; + rangeEndLineNumberEx += delta; + } else if (rangeStartLineNumber <= textEditStartLineNumber && textEditEndLineNumber < rangeStartLineNumber) { + // the text edit starts inside our range + rangeEndLineNumberEx = textEditStartLineNumber; + } else { + rangeEndLineNumberEx += delta; + } + } + + return new LineRange(rangeStartLineNumber, rangeEndLineNumberEx); +} + +function applyModifiedEditsToLineRangeMappings(changes: readonly LineRangeMapping[], textEdits: TextEditInfo[], originalTextModel: ITextModel, modifiedTextModel: ITextModel): LineRangeMapping[] { + const diffTextEdits = changes.flatMap(c => c.innerChanges!.map(c => new TextEditInfo( positionToLength(c.originalRange.getStartPosition()), positionToLength(c.originalRange.getEndPosition()), lengthOfRange(c.modifiedRange).toLength(), @@ -376,16 +424,10 @@ function applyModifiedEdits(diff: IDocumentDiff, textEdits: TextEditInfo[], orig ); }); - const changes = lineRangeMappingFromRangeMappings( + const newChanges = lineRangeMappingFromRangeMappings( rangeMappings, originalTextModel.getLinesContent(), modifiedTextModel.getLinesContent(), ); - - return { - identical: false, - quitEarly: false, - changes, - moves: [], - }; + return newChanges; } diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts index a979ecf78ca..e2d5bebe6f0 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts @@ -28,8 +28,8 @@ export class UnchangedRangesFeature extends Disposable { const m = this._diffModel.get(); transaction(tx => { for (const s of this._originalEditor.getSelections() || []) { - m?.revealOriginalLine(s.getStartPosition().lineNumber, tx); - m?.revealOriginalLine(s.getEndPosition().lineNumber, tx); + m?.ensureOriginalLineIsVisible(s.getStartPosition().lineNumber, tx); + m?.ensureOriginalLineIsVisible(s.getEndPosition().lineNumber, tx); } }); })); @@ -38,8 +38,8 @@ export class UnchangedRangesFeature extends Disposable { const m = this._diffModel.get(); transaction(tx => { for (const s of this._modifiedEditor.getSelections() || []) { - m?.revealModifiedLine(s.getStartPosition().lineNumber, tx); - m?.revealModifiedLine(s.getEndPosition().lineNumber, tx); + m?.ensureModifiedLineIsVisible(s.getStartPosition().lineNumber, tx); + m?.ensureModifiedLineIsVisible(s.getEndPosition().lineNumber, tx); } }); })); diff --git a/src/vs/editor/common/diff/linesDiffComputer.ts b/src/vs/editor/common/diff/linesDiffComputer.ts index e8fddf830a9..a096042419f 100644 --- a/src/vs/editor/common/diff/linesDiffComputer.ts +++ b/src/vs/editor/common/diff/linesDiffComputer.ts @@ -102,6 +102,10 @@ export class LineRangeMapping { public get changedLineCount() { return Math.max(this.originalRange.length, this.modifiedRange.length); } + + public flip(): LineRangeMapping { + return new LineRangeMapping(this.modifiedRange, this.originalRange, this.innerChanges?.map(c => c.flip())); + } } /** @@ -130,6 +134,10 @@ export class RangeMapping { public toString(): string { return `{${this.originalRange.toString()}->${this.modifiedRange.toString()}}`; } + + public flip(): RangeMapping { + return new RangeMapping(this.modifiedRange, this.originalRange); + } } export class SimpleLineRangeMapping { @@ -142,6 +150,10 @@ export class SimpleLineRangeMapping { public toString(): string { return `{${this.originalRange.toString()}->${this.modifiedRange.toString()}}`; } + + public flip(): SimpleLineRangeMapping { + return new SimpleLineRangeMapping(this.modifiedRange, this.originalRange); + } } export class MovedText { @@ -161,4 +173,8 @@ export class MovedText { this.lineRangeMapping = lineRangeMapping; this.changes = changes; } + + public flip(): MovedText { + return new MovedText(this.lineRangeMapping.flip(), this.changes.map(c => c.flip())); + } } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index f4f733504e9..e19a539a1f2 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2501,6 +2501,7 @@ declare namespace monaco.editor { constructor(originalRange: LineRange, modifiedRange: LineRange, innerChanges: RangeMapping[] | undefined); toString(): string; get changedLineCount(): any; + flip(): LineRangeMapping; } /** @@ -2517,6 +2518,7 @@ declare namespace monaco.editor { readonly modifiedRange: Range; constructor(originalRange: Range, modifiedRange: Range); toString(): string; + flip(): RangeMapping; } export class MovedText { @@ -2528,6 +2530,7 @@ declare namespace monaco.editor { */ readonly changes: readonly LineRangeMapping[]; constructor(lineRangeMapping: SimpleLineRangeMapping, changes: readonly LineRangeMapping[]); + flip(): MovedText; } export class SimpleLineRangeMapping { @@ -2535,6 +2538,7 @@ declare namespace monaco.editor { readonly modifiedRange: LineRange; constructor(originalRange: LineRange, modifiedRange: LineRange); toString(): string; + flip(): SimpleLineRangeMapping; } export interface IDimension { width: number; From f1b2a3a77faedb737630bf8a0e4df6bf00b276a0 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 13 Jun 2023 20:16:58 +0200 Subject: [PATCH 11/20] Use `Lazy` for creating diff and preview zone so that they don't block startup (#185041) https://github.com/microsoft/vscode/issues/185034 --- .../browser/inlineChatStrategies.ts | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts index 03bb8f3e7ea..d498c1f1c3a 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; +import { Lazy } from 'vs/base/common/lazy'; import { IDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; @@ -347,8 +348,8 @@ export class LiveStrategy extends EditModeStrategy { export class LivePreviewStrategy extends LiveStrategy { - private readonly _diffZone: InlineChatLivePreviewWidget; - private readonly _previewZone: InlineChatFileCreatePreviewWidget; + private readonly _diffZone: Lazy; + private readonly _previewZone: Lazy; constructor( session: Session, @@ -362,15 +363,15 @@ export class LivePreviewStrategy extends LiveStrategy { ) { super(session, editor, widget, contextKeyService, storageService, bulkEditService, editorWorkerService, instaService); - this._diffZone = instaService.createInstance(InlineChatLivePreviewWidget, editor, session); - this._previewZone = instaService.createInstance(InlineChatFileCreatePreviewWidget, editor); + this._diffZone = new Lazy(() => instaService.createInstance(InlineChatLivePreviewWidget, editor, session)); + this._previewZone = new Lazy(() => instaService.createInstance(InlineChatFileCreatePreviewWidget, editor)); } override dispose(): void { - this._diffZone.hide(); - this._diffZone.dispose(); - this._previewZone.hide(); - this._previewZone.dispose(); + this._diffZone.rawValue?.hide(); + this._diffZone.rawValue?.dispose(); + this._previewZone.rawValue?.hide(); + this._previewZone.rawValue?.dispose(); super.dispose(); } @@ -378,33 +379,33 @@ export class LivePreviewStrategy extends LiveStrategy { this._updateSummaryMessage(); if (this._diffEnabled) { - this._diffZone.show(); + this._diffZone.value.show(); } if (response.singleCreateFileEdit) { - this._previewZone.showCreation(this._session.wholeRange.value, response.singleCreateFileEdit.uri, await Promise.all(response.singleCreateFileEdit.edits)); + this._previewZone.value.showCreation(this._session.wholeRange.value, response.singleCreateFileEdit.uri, await Promise.all(response.singleCreateFileEdit.edits)); } else { - this._previewZone.hide(); + this._previewZone.value.hide(); } } override async undoChanges(response: EditResponse): Promise { - this._diffZone.lockToDiff(); + this._diffZone.value.lockToDiff(); super.undoChanges(response); } protected override _doToggleDiff(): void { const scrollState = StableEditorScrollState.capture(this._editor); if (this._diffEnabled) { - this._diffZone.show(); + this._diffZone.value.show(); } else { - this._diffZone.hide(); + this._diffZone.value.hide(); } scrollState.restore(this._editor); } override hasFocus(): boolean { - return super.hasFocus() || this._diffZone.hasFocus() || this._previewZone.hasFocus(); + return super.hasFocus() || this._diffZone.value.hasFocus() || this._previewZone.value.hasFocus(); } } From 2e335d2df2001439b09483e7fcff8379ac1a7a16 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Tue, 13 Jun 2023 11:46:04 -0700 Subject: [PATCH 12/20] Update payload size limit (#185052) --- .../contrib/editSessions/browser/editSessionsStorageService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts index 2f63475b58a..c7389a1b224 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts @@ -33,7 +33,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes declare _serviceBrand: undefined; - public readonly SIZE_LIMIT = 1024 * 1024 * 2; // 2 MB + public readonly SIZE_LIMIT = Math.floor(1024 * 1024 * 1.9); // 2 MB private serverConfiguration = this.productService['editSessions.store']; private machineClient: IUserDataSyncMachinesService | undefined; From b42cfb963cdb75b7be347f62ac38395b1a40c8e8 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 13 Jun 2023 15:38:16 -0500 Subject: [PATCH 13/20] rm copilot term --- .../browser/actions/chatAccessibilityHelp.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts b/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts index 65bbd5e8f97..76e92276042 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts @@ -13,22 +13,26 @@ import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; + +const inputBox = localize('chat.requestHistory', 'In the input box, use up and down arrows to navigate your request history. Edit input and use enter or the submit button to run a new request.'); + export function getAccessibilityHelpText(accessor: ServicesAccessor, type: 'chat' | 'inline'): string { const keybindingService = accessor.get(IKeybindingService); const content = []; if (type === 'chat') { - content.push(localize('chat.overview', 'Chat responses will be announced as they come in. A response will indicate the number of code blocks, if any, and then the rest of the response.')); - content.push(localize('chat.requestHistory', 'In the input box, use up and down arrows to navigate your request history. Edit input and use enter to run a new request.')); - content.push(descriptionForCommand('chat.action.focus', localize('workbench.action.chat.focus', 'The Focus Chat command ({0}) focuses the chat request/response list, which can be navigated with UpArrow/DownArrow.',), localize('workbench.action.chat.focusNoKb', 'The Focus Chat List command focuses the chat request/response list, which can be navigated with UpArrow/DownArrow and is currently not triggerable by a keybinding.'), keybindingService)); + content.push(localize('chat.overview', 'The chat view is comprised of an input box and a request/response list. The input box is used to make requests and the list is used to display responses.')); + content.push(inputBox); + content.push(localize('chat.announcement', 'Chat responses will be announced as they come in. A response will indicate the number of code blocks, if any, and then the rest of the response.')); + content.push(descriptionForCommand('chat.action.focus', localize('workbench.action.chat.focus', 'The Focus Chat command ({0}) focuses the chat request/response list, which can be navigated with up and down arrows.',), localize('workbench.action.chat.focusNoKb', 'The Focus Chat List command focuses the chat request/response list, which can be navigated with UpArrow/DownArrow and is currently not triggerable by a keybinding.'), keybindingService)); content.push(descriptionForCommand('workbench.action.chat.focusInput', localize('workbench.action.chat.focusInput', 'The Focus Chat Input command ({0}) focuses the input box for chat requests.'), localize('workbench.action.interactiveSession.focusInputNoKb', 'Focus Chat Input command focuses the input box for chat requests and is currently not triggerable by a keybinding.'), keybindingService)); content.push(descriptionForCommand('workbench.action.chat.nextCodeBlock', localize('workbench.action.chat.nextCodeBlock', 'The Chat: Next Code Block command ({0}) focuses the next code block within a response.'), localize('workbench.action.chat.nextCodeBlockNoKb', 'The Chat: Next Code Block command focuses the next code block within a response and is currently not triggerable by a keybinding.'), keybindingService)); content.push(descriptionForCommand('workbench.action.chat.clear', localize('workbench.action.chat.clear', 'The Chat Clear command ({0}) clears the request/response list.'), localize('workbench.action.chat.clearNoKb', 'The Chat Clear command clears the request/response list and is currently not triggerable by a keybinding.'), keybindingService)); } else { const startChatKeybinding = keybindingService.lookupKeybinding('inlineChat.start')?.getAriaLabel(); - content.push(localize('inlineChat.overview', "Inline chat occurs within a code editor and takes into account current selection. It is useful for refactoring, fixing, and more. Keep in mind that Copilot generated code may be incorrect.")); - content.push(localize('inlineChat.access', "It can be activated via the Fix and Explain with Copilot context menu actions or directly using the command: Inline Chat: Start Code Chat ({0}).", startChatKeybinding)); - content.push(localize('chat.requestHistoryInline', 'In the input box, use up and down arrows to navigate your request history. Edit input and use enter or the make request button to run a new request.')); - content.push(localize('inlineChat.contextActions', "Explain and Fix with Copilot actions run a request prefixed with /fix or /explain. These prefixes can be used directly in the input box to apply those specific actions.")); + content.push(localize('inlineChat.overview', "Inline chat occurs within a code editor and takes into account the current selection. It is useful for refactoring, fixing, and more. Keep in mind that AI generated code may be incorrect.")); + content.push(localize('inlineChat.access', "It can be activated via quick fix actions or directly using the command: Inline Chat: Start Code Chat ({0}).", startChatKeybinding)); + content.push(inputBox); + content.push(localize('inlineChat.contextActions', "Context menu actions may run a request prefixed with /fix or /explain. These prefixes can be used directly in the input box to apply those specific actions.")); content.push(localize('inlineChat.fix', "When a request is prefixed with /fix, a response will indicate the problem with the current code. A diff editor will be rendered and can be reached by tabbing.")); const diffReviewKeybinding = keybindingService.lookupKeybinding('editor.action.diffReview.next')?.getAriaLabel(); content.push(diffReviewKeybinding ? localize('inlineChat.diff', "Once in the diff editor, enter review mode with ({0}). Use up and down arrows to navigate lines with the proposed changes.", diffReviewKeybinding) : localize('inlineChat.diffNoKb', "Tab again to enter the Diff editor with the changes and enter review mode with the Go to Next Difference Command. Use Up/DownArrow to navigate lines with the proposed changes.")); From 34911a262b2d658b5050f67510a72669e8cc0a39 Mon Sep 17 00:00:00 2001 From: cadinsl <46573438+cadinsl@users.noreply.github.com> Date: Tue, 13 Jun 2023 17:06:45 -0400 Subject: [PATCH 14/20] Edited user and workspace tab to only display the name for accessibility #184530 (#184627) --- .../preferences/browser/preferencesWidgets.ts | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts index 067e5110b60..c4f4a6b1fdd 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts @@ -33,7 +33,6 @@ import { ThemeIcon } from 'vs/base/common/themables'; import { isWorkspaceFolder, IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { settingsEditIcon, settingsScopeDropDownIcon } from 'vs/workbench/contrib/preferences/browser/preferencesIcons'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { CONTEXT_SETTINGS_EDITOR_IN_USER_TAB } from 'vs/workbench/contrib/preferences/common/preferences'; @@ -52,7 +51,6 @@ export class FolderSettingsActionViewItem extends BaseActionViewItem { action: IAction, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IContextMenuService private readonly contextMenuService: IContextMenuService, - @IPreferencesService private readonly preferencesService: IPreferencesService, ) { super(null, action); const workspace = this.contextService.getWorkspace(); @@ -142,14 +140,14 @@ export class FolderSettingsActionViewItem extends BaseActionViewItem { } } - private async update(): Promise { + private update(): void { let total = 0; this._folderSettingCounts.forEach(n => total += n); const workspace = this.contextService.getWorkspace(); if (this._folder) { this.labelElement.textContent = this._folder.name; - this.anchorElement.title = (await this.preferencesService.getEditableSettingsURI(ConfigurationTarget.WORKSPACE_FOLDER, this._folder.uri))?.fsPath || ''; + this.anchorElement.title = this._folder.name; const detailsText = this.labelWithCount(this._action.label, total); this.detailsElement.textContent = detailsText; this.dropDownElement.classList.toggle('hide', workspace.folders.length === 1 || !this._action.checked); @@ -233,7 +231,6 @@ export class SettingsTargetsWidget extends Widget { @IInstantiationService private readonly instantiationService: IInstantiationService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @ILabelService private readonly labelService: ILabelService, - @IPreferencesService private readonly preferencesService: IPreferencesService, @ILanguageService private readonly languageService: ILanguageService, @IContextKeyService contextKeyService: IContextKeyService, ) { @@ -265,15 +262,12 @@ export class SettingsTargetsWidget extends Widget { })); this.userLocalSettings = new Action('userSettings', '', '.settings-tab', true, () => this.updateTarget(ConfigurationTarget.USER_LOCAL)); - this.preferencesService.getEditableSettingsURI(ConfigurationTarget.USER_LOCAL).then(uri => { - // Don't wait to create UI on resolving remote - this.userLocalSettings.tooltip = uri?.fsPath ?? ''; - }); + this.userLocalSettings.tooltip = localize('userSettings', "User"); this.userRemoteSettings = new Action('userSettingsRemote', '', '.settings-tab', true, () => this.updateTarget(ConfigurationTarget.USER_REMOTE)); - this.preferencesService.getEditableSettingsURI(ConfigurationTarget.USER_REMOTE).then(uri => { - this.userRemoteSettings.tooltip = uri?.fsPath ?? ''; - }); + const remoteAuthority = this.environmentService.remoteAuthority; + const hostLabel = remoteAuthority && this.labelService.getHostLabel(Schemas.vscodeRemote, remoteAuthority); + this.userRemoteSettings.tooltip = localize('userSettingsRemote', "Remote") + (hostLabel ? ` [${hostLabel}]` : ''); this.workspaceSettings = new Action('workspaceSettings', '', '.settings-tab', false, () => this.updateTarget(ConfigurationTarget.WORKSPACE)); @@ -368,7 +362,7 @@ export class SettingsTargetsWidget extends Widget { this.workspaceSettings.enabled = this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY; this.folderSettings.action.enabled = this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE && this.contextService.getWorkspace().folders.length > 0; - this.workspaceSettings.tooltip = (await this.preferencesService.getEditableSettingsURI(ConfigurationTarget.WORKSPACE))?.fsPath || ''; + this.workspaceSettings.tooltip = localize('workspaceSettings', "Workspace"); } } From 20d8459c1d98798429d70fae6da34ca556210eff Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 13 Jun 2023 23:38:24 +0200 Subject: [PATCH 15/20] Implements simple caching for diff editor v2 (#185060) --- src/vs/editor/browser/editorBrowser.ts | 4 +- .../editor/browser/widget/codeEditorWidget.ts | 2 +- .../editor/browser/widget/diffEditorWidget.ts | 15 ++++++- .../diffEditorWidget2/delegatingEditorImpl.ts | 4 +- .../diffEditorWidget2/diffEditorWidget2.ts | 42 ++++++++++++------- .../widget/diffEditorWidget2/diffModel.ts | 10 +++-- .../diffEditorWidget2/movedBlocksLines.ts | 5 +-- .../widget/workerBasedDocumentDiffProvider.ts | 15 +++++++ src/vs/editor/common/editorCommon.ts | 8 +++- src/vs/monaco.d.ts | 10 ++++- .../browser/parts/editor/textDiffEditor.ts | 7 +++- 11 files changed, 92 insertions(+), 30 deletions(-) diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 836448dac93..c85f63877e5 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -1157,6 +1157,8 @@ export interface IDiffEditor extends editorCommon.IEditor { */ getModel(): editorCommon.IDiffEditorModel | null; + createViewModel(model: editorCommon.IDiffEditorModel): editorCommon.IDiffEditorViewModel; + /** * Sets the current model attached to this editor. * If the previous model was created by the editor via the value key in the options @@ -1165,7 +1167,7 @@ export interface IDiffEditor extends editorCommon.IEditor { * will not be destroyed. * It is safe to call setModel(null) to simply detach the current model from the editor. */ - setModel(model: editorCommon.IDiffEditorModel | null): void; + setModel(model: editorCommon.IDiffEditorModel | editorCommon.IDiffEditorViewModel | null): void; /** * Get the `original` editor. diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 181b2c279e4..dedb0237589 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -488,7 +488,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return this._modelData.model; } - public setModel(_model: ITextModel | editorCommon.IDiffEditorModel | null = null): void { + public setModel(_model: ITextModel | editorCommon.IDiffEditorModel | editorCommon.IDiffEditorViewModel | null = null): void { const model = _model; if (this._modelData === null && model === null) { // Current model is the new model diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index e9d3e2d1ec1..f25d9b3f9b4 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -829,7 +829,20 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE }; } - public setModel(model: editorCommon.IDiffEditorModel | null): void { + public createViewModel(model: editorCommon.IDiffEditorModel): editorCommon.IDiffEditorViewModel { + return { + model, + async waitForDiff() { + // noop + }, + }; + } + + public setModel(model: editorCommon.IDiffEditorModel | editorCommon.IDiffEditorViewModel | null): void { + if (model && 'model' in model) { + model = model.model; + } + // Guard us against partial null model if (model && (!model.original || !model.modified)) { throw new Error(!model.original ? 'DiffEditorWidget.setModel: Original model is null' : 'DiffEditorWidget.setModel: Modified model is null'); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/delegatingEditorImpl.ts b/src/vs/editor/browser/widget/diffEditorWidget2/delegatingEditorImpl.ts index 70d9fe58399..4ab6f4201b8 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/delegatingEditorImpl.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/delegatingEditorImpl.ts @@ -11,7 +11,7 @@ import { IDimension } from 'vs/editor/common/core/dimension'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { ISelection, Selection } from 'vs/editor/common/core/selection'; -import { IEditor, IEditorAction, IEditorDecorationsCollection, IEditorModel, IEditorViewState, ScrollType } from 'vs/editor/common/editorCommon'; +import { IDiffEditorViewModel, IEditor, IEditorAction, IEditorDecorationsCollection, IEditorModel, IEditorViewState, ScrollType } from 'vs/editor/common/editorCommon'; import { IModelDecorationsChangeAccessor, IModelDeltaDecoration } from 'vs/editor/common/model'; export abstract class DelegatingEditor extends Disposable implements IEditor { @@ -34,7 +34,7 @@ export abstract class DelegatingEditor extends Disposable implements IEditor { abstract saveViewState(): IEditorViewState | null; abstract restoreViewState(state: IEditorViewState | null): void; abstract getModel(): IEditorModel | null; - abstract setModel(model: IEditorModel | null): void; + abstract setModel(model: IEditorModel | null | IDiffEditorViewModel): void; // #region editorBrowser.IDiffEditor: Delegating to modified Editor diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts index a4c4cc5821e..c4c9edf6990 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts @@ -7,8 +7,8 @@ import { IBoundarySashes } from 'vs/base/browser/ui/sash/sash'; import { findLast } from 'vs/base/common/arrays'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { IObservable, ISettableObservable, autorun, derived, keepAlive, observableValue, waitForState } from 'vs/base/common/observable'; -import { disposableObservableValue } from 'vs/base/common/observableImpl/base'; +import { IObservable, ISettableObservable, autorun, derived, keepAlive, observableValue } from 'vs/base/common/observable'; +import { disposableObservableValue, transaction } from 'vs/base/common/observableImpl/base'; import { derivedWithStore } from 'vs/base/common/observableImpl/derived'; import { isDefined } from 'vs/base/common/types'; import { Constants } from 'vs/base/common/uint'; @@ -32,7 +32,7 @@ import { IDimension } from 'vs/editor/common/core/dimension'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { Position } from 'vs/editor/common/core/position'; import { IDiffComputationResult, ILineChange } from 'vs/editor/common/diff/smartLinesDiffComputer'; -import { EditorType, IContentSizeChangedEvent, IDiffEditorModel, IDiffEditorViewState } from 'vs/editor/common/editorCommon'; +import { EditorType, IContentSizeChangedEvent, IDiffEditorModel, IDiffEditorViewModel, IDiffEditorViewState } from 'vs/editor/common/editorCommon'; import { IModelDeltaDecoration } from 'vs/editor/common/model'; import { localize } from 'vs/nls'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -482,22 +482,27 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { } } - override getModel(): IDiffEditorModel | null { return this._model.get(); } - - override setModel(model: IDiffEditorModel | null): void { - this._originalEditor.setModel(model ? model.original : null); - this._modifiedEditor.setModel(model ? model.modified : null); - - this._model.set(model, undefined); - - this._diffModel.set(model ? new DiffModel( + public createViewModel(model: IDiffEditorModel): IDiffEditorViewModel { + return new DiffModel( model, this._options.map(o => o.ignoreTrimWhitespace), this._options.map(o => o.maxComputationTime), this._options.map(o => o.experimental.collapseUnchangedRegions!), this._options.map(o => o.experimental.showMoves! && o.renderSideBySide), this._instantiationService.createInstance(WorkerBasedDocumentDiffProvider, this._options.get()) - ) : undefined, undefined); + ); + } + + override getModel(): IDiffEditorModel | null { return this._model.get(); } + + override setModel(model: IDiffEditorModel | null | IDiffEditorViewModel): void { + const vm = model ? ('model' in model) ? model : this.createViewModel(model) : undefined; + this._originalEditor.setModel(vm ? vm.model.original : null); + this._modifiedEditor.setModel(vm ? vm.model.modified : null); + transaction(tx => { + this._model.set(vm?.model ?? null, tx); + this._diffModel.set(vm as (DiffModel | undefined), tx); + }); } override updateOptions(_newOptions: IDiffEditorOptions): void { @@ -639,7 +644,7 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { return; } // wait for the diff computation to finish - waitForState(diffModel.isDiffUpToDate, s => s).then(() => { + this.waitForDiff().then(() => { const diffs = diffModel.diff.get()?.mappings; if (!diffs || diffs.length === 0) { return; @@ -647,6 +652,15 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { this._goTo(diffs[0]); }); } + + + public async waitForDiff(): Promise { + const diffModel = this._diffModel.get(); + if (!diffModel) { + return; + } + await diffModel.waitForDiff(); + } } function validateDiffEditorOptions(options: Readonly, defaults: ValidDiffEditorBaseOptions): ValidDiffEditorBaseOptions { diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffModel.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffModel.ts index 4ef2d808a89..6094cee8684 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffModel.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffModel.ts @@ -5,7 +5,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IObservable, IReader, ITransaction, derived, observableSignal, observableSignalFromEvent, observableValue, transaction } from 'vs/base/common/observable'; +import { IObservable, IReader, ITransaction, derived, observableSignal, observableSignalFromEvent, observableValue, transaction, waitForState } from 'vs/base/common/observable'; import { autorunWithStore2 } from 'vs/base/common/observableImpl/autorun'; import { isDefined } from 'vs/base/common/types'; import { LineRange } from 'vs/editor/common/core/lineRange'; @@ -13,13 +13,13 @@ import { Range } from 'vs/editor/common/core/range'; import { IDocumentDiff, IDocumentDiffProvider } from 'vs/editor/common/diff/documentDiffProvider'; import { LineRangeMapping, MovedText, RangeMapping, SimpleLineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; import { lineRangeMappingFromRangeMappings } from 'vs/editor/common/diff/standardLinesDiffComputer'; -import { IDiffEditorModel } from 'vs/editor/common/editorCommon'; +import { IDiffEditorModel, IDiffEditorViewModel } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import { TextEditInfo } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/beforeEditPositionMapper'; import { combineTextEditInfos } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/combineTextEditInfos'; import { lengthAdd, lengthDiffNonNegative, lengthGetLineCount, lengthOfRange, lengthToPosition, lengthZero, positionToLength } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/length'; -export class DiffModel extends Disposable { +export class DiffModel extends Disposable implements IDiffEditorViewModel { private readonly _isDiffUpToDate = observableValue('isDiffUpToDate', false); public readonly isDiffUpToDate: IObservable = this._isDiffUpToDate; @@ -184,6 +184,10 @@ export class DiffModel extends Disposable { } } } + + public async waitForDiff(): Promise { + await waitForState(this.isDiffUpToDate, s => s); + } } export class DiffState { diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts index 5502568cc69..3df9808e6b0 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts @@ -42,8 +42,9 @@ export class MovedBlocksLinesPart extends Disposable { const originalScrollTop = observableFromEvent(this._originalEditor.onDidScrollChange, () => this._originalEditor.getScrollTop()); const modifiedScrollTop = observableFromEvent(this._modifiedEditor.onDidScrollChange, () => this._modifiedEditor.getScrollTop()); - this._register(autorun('update', (reader) => { + element.replaceChildren(); + const info = this._originalEditorLayoutInfo.read(reader); const info2 = this._modifiedEditorLayoutInfo.read(reader); if (!info || !info2) { @@ -56,8 +57,6 @@ export class MovedBlocksLinesPart extends Disposable { return; } - element.replaceChildren(); - let idx = 0; for (const m of moves) { function computeLineStart(range: LineRange, editor: ICodeEditor) { diff --git a/src/vs/editor/browser/widget/workerBasedDocumentDiffProvider.ts b/src/vs/editor/browser/widget/workerBasedDocumentDiffProvider.ts index a2fc75d5296..eddfb9c0ad9 100644 --- a/src/vs/editor/browser/widget/workerBasedDocumentDiffProvider.ts +++ b/src/vs/editor/browser/widget/workerBasedDocumentDiffProvider.ts @@ -20,6 +20,8 @@ export class WorkerBasedDocumentDiffProvider implements IDocumentDiffProvider, I private diffAlgorithm: DiffAlgorithmName | IDocumentDiffProvider = 'advanced'; private diffAlgorithmOnDidChangeSubscription: IDisposable | undefined = undefined; + private static readonly diffCache = new Map(); + constructor( options: IWorkerBasedDocumentDiffProviderOptions, @IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService, @@ -58,6 +60,13 @@ export class WorkerBasedDocumentDiffProvider implements IDocumentDiffProvider, I }; } + const uriKey = JSON.stringify([original.uri.toString(), modified.uri.toString()]); + const context = JSON.stringify([original.id, modified.id, original.getAlternativeVersionId(), modified.getAlternativeVersionId(), JSON.stringify(options)]); + const c = WorkerBasedDocumentDiffProvider.diffCache.get(uriKey); + if (c && c.context === context) { + return c.result; + } + const sw = StopWatch.create(true); const result = await this.editorWorkerService.computeDiff(original.uri, modified.uri, options, this.diffAlgorithm); const timeMs = sw.elapsed(); @@ -81,6 +90,12 @@ export class WorkerBasedDocumentDiffProvider implements IDocumentDiffProvider, I throw new Error('no diff result available'); } + // max 10 items in cache + if (WorkerBasedDocumentDiffProvider.diffCache.size > 10) { + WorkerBasedDocumentDiffProvider.diffCache.delete(WorkerBasedDocumentDiffProvider.diffCache.keys().next().value); + } + + WorkerBasedDocumentDiffProvider.diffCache.set(uriKey, { result, context }); return result; } diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index 2f735614140..e3b7854e21d 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -104,6 +104,12 @@ export interface IDiffEditorModel { modified: ITextModel; } +export interface IDiffEditorViewModel { + readonly model: IDiffEditorModel; + + waitForDiff(): Promise; +} + /** * An event describing that an editor has had its model reset (i.e. `editor.setModel()`). */ @@ -153,7 +159,7 @@ export interface IEditorAction { run(args?: unknown): Promise; } -export type IEditorModel = ITextModel | IDiffEditorModel; +export type IEditorModel = ITextModel | IDiffEditorModel | IDiffEditorViewModel; /** * A (serializable) state of the cursors. diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index e19a539a1f2..07aec862a82 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2623,6 +2623,11 @@ declare namespace monaco.editor { modified: ITextModel; } + export interface IDiffEditorViewModel { + readonly model: IDiffEditorModel; + waitForDiff(): Promise; + } + /** * An event describing that an editor has had its model reset (i.e. `editor.setModel()`). */ @@ -2657,7 +2662,7 @@ declare namespace monaco.editor { run(args?: unknown): Promise; } - export type IEditorModel = ITextModel | IDiffEditorModel; + export type IEditorModel = ITextModel | IDiffEditorModel | IDiffEditorViewModel; /** * A (serializable) state of the cursors. @@ -6026,6 +6031,7 @@ declare namespace monaco.editor { * Type the getModel() of IEditor. */ getModel(): IDiffEditorModel | null; + createViewModel(model: IDiffEditorModel): IDiffEditorViewModel; /** * Sets the current model attached to this editor. * If the previous model was created by the editor via the value key in the options @@ -6034,7 +6040,7 @@ declare namespace monaco.editor { * will not be destroyed. * It is safe to call setModel(null) to simply detach the current model from the editor. */ - setModel(model: IDiffEditorModel | null): void; + setModel(model: IDiffEditorModel | IDiffEditorViewModel | null): void; /** * Get the `original` editor. */ diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index 2ce5f1694c8..e5918fb80ca 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -5,7 +5,7 @@ import { localize } from 'vs/nls'; import { deepClone } from 'vs/base/common/objects'; -import { isObject, assertIsDefined, withUndefinedAsNull, withNullAsUndefined } from 'vs/base/common/types'; +import { isObject, assertIsDefined, withNullAsUndefined } from 'vs/base/common/types'; import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { IDiffEditorOptions, IEditorOptions as ICodeEditorOptions } from 'vs/editor/common/config/editorOptions'; import { AbstractTextEditor, IEditorConfiguration } from 'vs/workbench/browser/parts/editor/textEditor'; @@ -133,7 +133,10 @@ export class TextDiffEditor extends AbstractTextEditor imp // Set Editor Model const control = assertIsDefined(this.diffEditorControl); const resolvedDiffEditorModel = resolvedModel as TextDiffEditorModel; - control.setModel(withUndefinedAsNull(resolvedDiffEditorModel.textDiffEditorModel)); + + const vm = resolvedDiffEditorModel.textDiffEditorModel ? control.createViewModel(resolvedDiffEditorModel.textDiffEditorModel) : null; + await vm?.waitForDiff(); + control.setModel(vm); // Restore view state (unless provided by options) let hasPreviousViewState = false; From 990d40aad4e5fe57500132cae7ad8c69950399b0 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 13 Jun 2023 15:53:21 -0700 Subject: [PATCH 16/20] Fix highlight on chat thumbs buttons (#185063) The parent element used to also have the 'checked' class, now it doesn't, bisecting to figure out what happened Fix microsoft/vscode-copilot-release#242 --- src/vs/workbench/contrib/chat/browser/media/chat.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index d32c164481f..6796d8436a8 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -82,8 +82,8 @@ padding: 2px; } -.interactive-item-container .header .monaco-toolbar .checked .action-label, -.interactive-item-container .header .monaco-toolbar .checked .action-label:hover { +.interactive-item-container .header .monaco-toolbar .checked.action-label, +.interactive-item-container .header .monaco-toolbar .checked.action-label:hover { color: var(--vscode-inputOption-activeForeground) !important; border-color: var(--vscode-inputOption-activeBorder); background-color: var(--vscode-inputOption-activeBackground); From df2952cc9be54bd0b3450cca00dfe7bf7058499d Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 14 Jun 2023 10:25:15 +1000 Subject: [PATCH 17/20] Revert changes to listWidtget (#185071) --- src/vs/base/browser/ui/list/listWidget.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 2a282e9f1a3..d039f788cca 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -152,7 +152,8 @@ class Trait implements ISpliceable, IDisposable { const length = this.length + diff; if (this.sortedIndexes.length > 0 && sortedIndexes.length === 0 && length > 0) { - sortedIndexes.push(Math.min(firstSortedIndex ?? length - 1, length - 1)); + const first = this.sortedIndexes.find(index => index >= start) ?? length - 1; + sortedIndexes.push(Math.min(first, length - 1)); } this.renderer.splice(start, deleteCount, elements.length); From 352d2d3934fd0d2e62cd3992038cc6476f7e8b45 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 13 Jun 2023 17:45:22 -0700 Subject: [PATCH 18/20] Fix #184606. Fix updateCellMetadata incremental update. (#184964) --- src/vs/workbench/api/common/extHostTypes.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index f414312d433..e90c15ce050 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -17,7 +17,7 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from 'vs/platform/files/common/files'; import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IRelativePatternDto } from 'vs/workbench/api/common/extHost.protocol'; -import { CellEditType, ICellPartialMetadataEdit, IDocumentMetadataEdit, isTextStreamMime } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, ICellMetadataEdit, IDocumentMetadataEdit, isTextStreamMime } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import type * as vscode from 'vscode'; @@ -775,7 +775,7 @@ export interface IFileSnippetTextEdit { export interface IFileCellEdit { readonly _type: FileEditType.Cell; readonly uri: URI; - readonly edit?: ICellPartialMetadataEdit | IDocumentMetadataEdit; + readonly edit?: ICellMetadataEdit | IDocumentMetadataEdit; readonly notebookMetadata?: Record; readonly metadata?: vscode.WorkspaceEditEntryMetadata; } @@ -832,7 +832,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit { } private replaceNotebookCellMetadata(uri: URI, index: number, cellMetadata: Record, metadata?: vscode.WorkspaceEditEntryMetadata): void { - this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.PartialMetadata, index, metadata: cellMetadata } }); + this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.Metadata, index, metadata: cellMetadata } }); } // --- text From bf5419f02d146b4359c32fa0748f8ba47adf118c Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 13 Jun 2023 21:24:18 -0700 Subject: [PATCH 19/20] Update execution timer priorty to reserve some room. (#185076) --- .../contrib/cellStatusBar/executionStatusBarItemController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/executionStatusBarItemController.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/executionStatusBarItemController.ts index c09f41176fa..3aa72fc3bfb 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/executionStatusBarItemController.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/executionStatusBarItemController.ts @@ -321,7 +321,7 @@ class TimerCellStatusBarItem extends Disposable { return { text: formatCellDuration(duration, false), alignment: CellStatusbarAlignment.Left, - priority: Number.MAX_SAFE_INTEGER - 1, + priority: Number.MAX_SAFE_INTEGER - 5, tooltip }; } From 8e26c332debacb3c06a36fee410723e71700528a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 14 Jun 2023 08:43:26 +0200 Subject: [PATCH 20/20] Warning message doesn't show up when code-insiders --status is run while no instances are open (#185080) * Warning message doesn't show up when `code-insiders --status` is run while no instances are open (fix #185069) * :lipstick: --- src/vs/code/electron-main/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index adc1cba1945..6e960fc47c7 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -382,7 +382,7 @@ class CodeMain { // Print --status usage info if (environmentMainService.args.status) { - logService.warn(localize('statusWarning', "Warning: The --status argument can only be used if {0} is already running. Please run it again after {0} has started.", productService.nameShort)); + console.log(localize('statusWarning', "Warning: The --status argument can only be used if {0} is already running. Please run it again after {0} has started.", productService.nameShort)); throw new ExpectedError('Terminating...'); }