Skip to content

Commit 5ae6f1e

Browse files
authored
chat overlay fixes (microsoft#236079)
* fixes disabled button color in chat overlay * fixes microsoft/vscode-copilot#10930 * fix reveal after streaming issues
1 parent 1a56b0b commit 5ae6f1e

File tree

3 files changed

+78
-71
lines changed

3 files changed

+78
-71
lines changed

src/vs/workbench/contrib/chat/browser/chatEditorController.ts

+57-49
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import './media/chatEditorController.css';
77
import { addStandardDisposableListener, getTotalWidth } from '../../../../base/browser/dom.js';
88
import { Disposable, DisposableStore, dispose, toDisposable } from '../../../../base/common/lifecycle.js';
9-
import { autorun, autorunWithStore, derived, IObservable, observableFromEvent, observableValue } from '../../../../base/common/observable.js';
9+
import { autorun, autorunWithStore, derived, IObservable, observableFromEvent, observableValue, transaction } from '../../../../base/common/observable.js';
1010
import { themeColorFromId } from '../../../../base/common/themables.js';
1111
import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, IOverlayWidgetPositionCoordinates, IViewZone, MouseTargetType } from '../../../../editor/browser/editorBrowser.js';
1212
import { LineSource, renderLines, RenderOptions } from '../../../../editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js';
@@ -55,8 +55,11 @@ export class ChatEditorController extends Disposable implements IEditorContribut
5555
return controller;
5656
}
5757

58-
private readonly _currentChange = observableValue<Position | undefined>(this, undefined);
59-
readonly currentChange: IObservable<Position | undefined> = this._currentChange;
58+
private readonly _currentEntryIndex = observableValue<number | undefined>(this, undefined);
59+
readonly currentEntryIndex: IObservable<number | undefined> = this._currentEntryIndex;
60+
61+
private readonly _currentChangeIndex = observableValue<number | undefined>(this, undefined);
62+
readonly currentChangeIndex: IObservable<number | undefined> = this._currentChangeIndex;
6063

6164
private _scrollLock: boolean = false;
6265

@@ -76,20 +79,28 @@ export class ChatEditorController extends Disposable implements IEditorContribut
7679
const lineHeightObs = observableCodeEditor(this._editor).getOption(EditorOption.lineHeight);
7780
const modelObs = observableCodeEditor(this._editor).model;
7881

79-
// scroll along unless "another" scroll happens
80-
let ignoreScrollEvent = false;
81-
this._store.add(this._editor.onDidScrollChange(e => {
82-
if (e.scrollTopChanged && !ignoreScrollEvent) {
83-
// this._scrollLock = true;
84-
}
85-
}));
8682

8783
this._store.add(autorun(r => {
8884
const session = this._chatEditingService.currentEditingSessionObs.read(r);
8985
this._ctxRequestInProgress.set(session?.state.read(r) === ChatEditingSessionState.StreamingEdits);
9086
}));
9187

92-
let didReveal = false;
88+
89+
const entryForEditor = derived(r => {
90+
const model = modelObs.read(r);
91+
const session = this._chatEditingService.currentEditingSessionObs.read(r);
92+
if (!session) {
93+
return undefined;
94+
}
95+
const entry = model?.uri ? session.readEntry(model.uri, r) : undefined;
96+
if (!entry || entry.state.read(r) !== WorkingSetEntryState.Modified) {
97+
return undefined;
98+
}
99+
return { session, entry };
100+
});
101+
102+
103+
let didReval = false;
93104

94105
this._register(autorunWithStore((r, store) => {
95106

@@ -98,55 +109,48 @@ export class ChatEditorController extends Disposable implements IEditorContribut
98109
return;
99110
}
100111

101-
fontInfoObs.read(r);
102-
lineHeightObs.read(r);
103-
104-
const model = modelObs.read(r);
112+
const currentEditorEntry = entryForEditor.read(r);
105113

106-
const session = this._chatEditingService.currentEditingSessionObs.read(r);
107-
const entry = model?.uri ? session?.readEntry(model.uri, r) : undefined;
108-
109-
if (!entry || entry.state.read(r) !== WorkingSetEntryState.Modified) {
114+
if (!currentEditorEntry) {
110115
this._clearRendering();
111-
didReveal = false;
116+
didReval = false;
112117
return;
113118
}
114119

120+
const { session, entry } = currentEditorEntry;
121+
122+
const entryIndex = session.entries.read(r).indexOf(entry);
123+
this._currentEntryIndex.set(entryIndex, undefined);
124+
115125
if (entry.isCurrentlyBeingModified.read(r)) {
126+
// while modified: scroll along unless locked
127+
if (!this._scrollLock) {
128+
const maxLineNumber = entry.maxLineNumber.read(r);
129+
this._editor.revealLineNearTop(maxLineNumber, ScrollType.Smooth);
130+
}
116131
const domNode = this._editor.getDomNode();
117132
if (domNode) {
118133
store.add(addStandardDisposableListener(domNode, 'wheel', () => {
119134
this._scrollLock = true;
120135
}));
121136
}
122-
}
137+
} else {
138+
// done: render diff
139+
fontInfoObs.read(r);
140+
lineHeightObs.read(r);
123141

124-
try {
125-
ignoreScrollEvent = true;
126-
if (!this._scrollLock && !didReveal) {
127-
const maxLineNumber = entry.maxLineNumber.read(r);
128-
this._editor.revealLineNearTop(maxLineNumber, ScrollType.Smooth);
129-
}
130142
const diff = entry?.diffInfo.read(r);
131143
this._updateWithDiff(entry, diff);
132-
if (!entry.isCurrentlyBeingModified.read(r)) {
133-
this.initNavigation();
134-
135-
if (!this._scrollLock && !didReveal) {
136-
const currentPosition = this._currentChange.read(r);
137-
if (currentPosition) {
138-
this._editor.revealLine(currentPosition.lineNumber, ScrollType.Smooth);
139-
didReveal = true;
140-
} else {
141-
didReveal = this.revealNext();
142-
}
143-
}
144+
145+
if (!didReval && !diff.identical) {
146+
didReval = true;
147+
this.revealNext();
144148
}
145-
} finally {
146-
ignoreScrollEvent = false;
147149
}
148150
}));
149151

152+
// ---- readonly while streaming
153+
150154
const shouldBeReadOnly = derived(this, r => {
151155
const value = this._chatEditingService.currentEditingSessionObs.read(r);
152156
if (!value || value.state.read(r) !== ChatEditingSessionState.StreamingEdits) {
@@ -200,7 +204,10 @@ export class ChatEditorController extends Disposable implements IEditorContribut
200204
this._diffVisualDecorations.clear();
201205
this._diffLineDecorations.clear();
202206
this._ctxHasEditorModification.reset();
203-
this._currentChange.set(undefined, undefined);
207+
transaction(tx => {
208+
this._currentEntryIndex.set(undefined, tx);
209+
this._currentChangeIndex.set(undefined, tx);
210+
});
204211
this._scrollLock = false;
205212
}
206213

@@ -423,10 +430,8 @@ export class ChatEditorController extends Disposable implements IEditorContribut
423430

424431
initNavigation(): void {
425432
const position = this._editor.getPosition();
426-
const range = position && this._diffLineDecorations.getRanges().find(r => r.containsPosition(position));
427-
if (range) {
428-
this._currentChange.set(position, undefined);
429-
}
433+
const target = position ? this._diffLineDecorations.getRanges().findIndex(r => r.containsPosition(position)) : -1;
434+
this._currentChangeIndex.set(target >= 0 ? target : undefined, undefined);
430435
}
431436

432437
revealNext(strict = false): boolean {
@@ -440,6 +445,7 @@ export class ChatEditorController extends Disposable implements IEditorContribut
440445
private _reveal(next: boolean, strict: boolean): boolean {
441446
const position = this._editor.getPosition();
442447
if (!position) {
448+
this._currentChangeIndex.set(undefined, undefined);
443449
return false;
444450
}
445451

@@ -448,6 +454,7 @@ export class ChatEditorController extends Disposable implements IEditorContribut
448454
.sort((a, b) => Range.compareRangesUsingStarts(a, b));
449455

450456
if (decorations.length === 0) {
457+
this._currentChangeIndex.set(undefined, undefined);
451458
return false;
452459
}
453460

@@ -464,18 +471,19 @@ export class ChatEditorController extends Disposable implements IEditorContribut
464471
}
465472

466473
if (strict && (target < 0 || target >= decorations.length)) {
474+
this._currentChangeIndex.set(undefined, undefined);
467475
return false;
468476
}
469477

470478
target = (target + decorations.length) % decorations.length;
471479

472-
const targetPosition = next ? decorations[target].getStartPosition() : decorations[target].getEndPosition();
473-
474-
this._currentChange.set(targetPosition, undefined);
480+
this._currentChangeIndex.set(target, undefined);
475481

482+
const targetPosition = next ? decorations[target].getStartPosition() : decorations[target].getEndPosition();
476483
this._editor.setPosition(targetPosition);
477484
this._editor.revealPositionInCenter(targetPosition, ScrollType.Smooth);
478485
this._editor.focus();
486+
479487
return true;
480488
}
481489

src/vs/workbench/contrib/chat/browser/chatEditorOverlay.ts

+15-16
Original file line numberDiff line numberDiff line change
@@ -203,25 +203,24 @@ class ChatEditorOverlayWidget implements IOverlayWidget {
203203

204204
this._showStore.add(autorun(r => {
205205

206-
const position = ChatEditorController.get(this._editor)?.currentChange.read(r);
206+
const ctrl = ChatEditorController.get(this._editor);
207+
208+
const entryIndex = ctrl?.currentEntryIndex.read(r);
209+
const changeIndex = ctrl?.currentChangeIndex.read(r);
210+
207211
const entries = session.entries.read(r);
208212

213+
let activeIdx = entryIndex !== undefined && changeIndex !== undefined
214+
? changeIndex
215+
: -1;
216+
209217
let changes = 0;
210-
let activeIdx = -1;
211-
for (const entry of entries) {
212-
const diffInfo = entry.diffInfo.read(r);
213-
214-
if (activeIdx !== -1 || entry !== activeEntry) {
215-
// just add up
216-
changes += diffInfo.changes.length;
217-
218-
} else {
219-
for (const change of diffInfo.changes) {
220-
if (position && change.modified.includes(position.lineNumber)) {
221-
activeIdx = changes;
222-
}
223-
changes += 1;
224-
}
218+
for (let i = 0; i < entries.length; i++) {
219+
const diffInfo = entries[i].diffInfo.read(r);
220+
changes += diffInfo.changes.length;
221+
222+
if (entryIndex !== undefined && i < entryIndex) {
223+
activeIdx += diffInfo.changes.length;
225224
}
226225
}
227226

src/vs/workbench/contrib/chat/browser/media/chatEditorOverlay.css

+6-6
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@
5353
color: var(--vscode-button-foreground);
5454
}
5555

56-
.chat-editor-overlay-widget .action-item.disabled > .action-label.codicon::before,
57-
.chat-editor-overlay-widget .action-item.disabled > .action-label.codicon,
58-
.chat-editor-overlay-widget .action-item.disabled > .action-label,
59-
.chat-editor-overlay-widget .action-item.disabled > .action-label:hover {
56+
.chat-editor-overlay-widget .monaco-action-bar .action-item.disabled > .action-label.codicon::before,
57+
.chat-editor-overlay-widget .monaco-action-bar .action-item.disabled > .action-label.codicon,
58+
.chat-editor-overlay-widget .monaco-action-bar .action-item.disabled > .action-label,
59+
.chat-editor-overlay-widget .monaco-action-bar .action-item.disabled > .action-label:hover {
6060
color: var(--vscode-button-foreground);
6161
opacity: 0.7;
6262
}
@@ -66,8 +66,8 @@
6666
font-variant-numeric: tabular-nums;
6767
}
6868

69-
.chat-editor-overlay-widget .action-item.label-item > .action-label,
70-
.chat-editor-overlay-widget .action-item.label-item > .action-label:hover {
69+
.chat-editor-overlay-widget .monaco-action-bar .action-item.label-item > .action-label,
70+
.chat-editor-overlay-widget .monaco-action-bar .action-item.label-item > .action-label:hover {
7171
color: var(--vscode-button-foreground);
7272
opacity: 1;
7373
}

0 commit comments

Comments
 (0)