Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: optimize inline diff viewer #3836

Merged
merged 26 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ada2e26
feat: optimize inline diff viewer
bytemain Jul 5, 2024
ee24f57
chore: update code
bytemain Jul 5, 2024
80991f9
chore: fix typo
bytemain Jul 5, 2024
bb57bd9
fix: discard
Ricbet Jul 5, 2024
9f9a227
fix: content widget handle
Ricbet Jul 5, 2024
958558b
chore: improve prioritize those closest to the head
Ricbet Jul 5, 2024
86be544
chore: improve layout code
Ricbet Jul 5, 2024
7010626
fix: removed widget position
Ricbet Jul 5, 2024
f14de91
feat: support setValue
Ricbet Jul 5, 2024
511d9a1
fix: inlineContentWidget
Ricbet Jul 5, 2024
cf2d0cd
Merge remote-tracking branch 'origin/feat/support-redo-re-render' int…
bytemain Jul 5, 2024
f5aba17
Merge remote-tracking branch 'origin/main' into feat/diff-viewer-optmize
bytemain Jul 5, 2024
1b5eb7d
chore: event report
Ricbet Jul 5, 2024
4f4eb5e
fix: check inline content widget
bytemain Jul 5, 2024
bab2ec0
chore: update code
bytemain Jul 5, 2024
a86907e
feat: dispose all diff previewer
bytemain Jul 5, 2024
c6be2a1
fix: event track
bytemain Jul 5, 2024
10f5c02
fix: recompute use default mode
bytemain Jul 5, 2024
91809b3
chore: update code
bytemain Jul 11, 2024
33ae626
Merge branch 'main' into feat/diff-viewer-optmize
bytemain Jul 11, 2024
8a5e294
chore: update code
bytemain Jul 11, 2024
6afb8d1
refactor: add inline diff service
bytemain Jul 11, 2024
a3098a2
fix: widget not show
bytemain Jul 11, 2024
6e40b77
chore: update code
bytemain Jul 11, 2024
6fe2781
fix: accept button cannot show
bytemain Jul 11, 2024
681b327
chore: update code
bytemain Jul 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/ai-native/src/browser/preferences/schema.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AINativeSettingSectionsId, PreferenceSchema } from '@opensumi/ide-core-browser';
import { localize } from '@opensumi/ide-core-common';

export enum EInlineDifPreviewMode {
export enum EInlineDiffPreviewMode {
inlineLive = 'inlineLive',
sideBySide = 'sideBySide',
}
Expand All @@ -10,12 +10,12 @@ export const aiNativePreferenceSchema: PreferenceSchema = {
properties: {
[AINativeSettingSectionsId.InlineDiffPreviewMode]: {
type: 'string',
enum: [EInlineDifPreviewMode.inlineLive, EInlineDifPreviewMode.sideBySide],
enum: [EInlineDiffPreviewMode.inlineLive, EInlineDiffPreviewMode.sideBySide],
enumDescriptions: [
localize('preference.ai.native.inlineDiff.preview.mode.inlineLive'),
localize('preference.ai.native.inlineDiff.preview.mode.sideBySide'),
],
default: EInlineDifPreviewMode.inlineLive,
default: EInlineDiffPreviewMode.inlineLive,
},
[AINativeSettingSectionsId.InlineChatAutoVisible]: {
type: 'boolean',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class InlineChatController {

constructor(readonly options?: InlineChatControllerOptions) {}

public deffered: Deferred<void> = new Deferred();
public deferred: Deferred<void> = new Deferred();

private calculateCodeBlocks(content: string): string {
if (!this.options?.enableCodeblockRender) {
Expand Down Expand Up @@ -70,7 +70,7 @@ export class InlineChatController {
}

public async mountReadable(stream: SumiReadableStream<IChatProgress>): Promise<void> {
await this.deffered.promise;
await this.deferred.promise;
const reply = new ReplyResponse('');
let wholeContent = '';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
CancellationTokenSource,
ChatResponse,
Disposable,
Emitter,
ErrorResponse,
Event,
IAIReporter,
Expand All @@ -28,7 +29,7 @@ import { monacoApi } from '@opensumi/ide-monaco/lib/browser/monaco-api';
import { ContentWidgetPositionPreference } from '@opensumi/ide-monaco/lib/browser/monaco-exports/editor';

import { CodeActionService } from '../../contrib/code-action/code-action.service';
import { EInlineDifPreviewMode } from '../../preferences/schema';
import { EInlineDiffPreviewMode } from '../../preferences/schema';
import { ERunStrategy } from '../../types';
import {
BaseInlineDiffPreviewer,
Expand All @@ -37,6 +38,7 @@ import {
} from '../inline-diff/inline-diff-previewer';
import { InlineDiffWidget } from '../inline-diff/inline-diff-widget';
import { InlineStreamDiffHandler } from '../inline-stream-diff/inline-stream-diff.handler';
import { IPartialEditEvent } from '../inline-stream-diff/live-preview.decoration';

import { InlineChatController } from './inline-chat-controller';
import { InlineChatFeatureRegistry } from './inline-chat.feature.registry';
Expand Down Expand Up @@ -72,6 +74,9 @@ export class InlineChatHandler extends Disposable {
@Autowired(CodeActionService)
private readonly codeActionService: CodeActionService;

private readonly _onPartialEditEvent = this.registerDispose(new Emitter<IPartialEditEvent>());
public readonly onPartialEditEvent: Event<IPartialEditEvent> = this._onPartialEditEvent.event;

private logger: ILogServiceClient;

private diffPreviewer: BaseInlineDiffPreviewer<InlineDiffWidget | InlineStreamDiffHandler>;
Expand Down Expand Up @@ -298,6 +303,10 @@ export class InlineChatHandler extends Disposable {
isStop?: boolean;
},
): void {
if (!this.aiInlineContentWidget) {
return;
}

const { relationId, message, startTime, isRetry, isStop } = reportInfo;

this.aiInlineChatDisposed.addDispose(this.aiInlineContentWidget.launchChatStatus(status));
Expand All @@ -310,7 +319,7 @@ export class InlineChatHandler extends Disposable {
});
}

private visibleDiffWidget(
visibleDiffWidget(
monacoEditor: monaco.ICodeEditor,
options: {
crossSelection: monaco.Selection;
Expand All @@ -325,17 +334,26 @@ export class InlineChatHandler extends Disposable {
const { crossSelection, chatResponse } = options;
const { relationId, startTime, isRetry } = reportInfo;

const inlineDiffMode = this.preferenceService.getValid<EInlineDifPreviewMode>(
const inlineDiffMode = this.preferenceService.getValid<EInlineDiffPreviewMode>(
AINativeSettingSectionsId.InlineDiffPreviewMode,
EInlineDifPreviewMode.inlineLive,
EInlineDiffPreviewMode.inlineLive,
);

if (inlineDiffMode === EInlineDifPreviewMode.sideBySide) {
if (!this.aiInlineChatDisposed.disposed) {
this.aiInlineChatDisposed.dispose();
}

if (inlineDiffMode === EInlineDiffPreviewMode.sideBySide) {
this.diffPreviewer = this.injector.get(SideBySideInlineDiffWidget, [monacoEditor, crossSelection]);
} else {
this.diffPreviewer = this.injector.get(LiveInlineDiffPreviewer, [monacoEditor, crossSelection]);
this.aiInlineChatDisposed.addDispose(
(this.diffPreviewer as LiveInlineDiffPreviewer).onPartialEditEvent((event) => {
this._onPartialEditEvent.fire(event);
}),
);
}

this.aiInlineChatDisposed.addDispose(this.diffPreviewer);
this.diffPreviewer.mount(this.aiInlineContentWidget);

this.diffPreviewer.show(
Expand All @@ -348,7 +366,7 @@ export class InlineChatHandler extends Disposable {

this.aiInlineChatOperationDisposed.addDispose(
this.diffPreviewer.onReady(() => {
controller.deffered.resolve();
controller.deferred.resolve();

this.aiInlineChatOperationDisposed.addDispose([
controller.onData((data) => {
Expand Down Expand Up @@ -394,7 +412,7 @@ export class InlineChatHandler extends Disposable {
const model = monacoEditor.getModel();
const crossCode = model!.getValueInRange(crossSelection);

if (this.aiInlineChatDisposed.disposed || CancelResponse.is(chatResponse)) {
if ((this.aiInlineContentWidget && this.aiInlineChatDisposed.disposed) || CancelResponse.is(chatResponse)) {
this.convertInlineChatStatus(EInlineChatStatus.READY, {
relationId,
message: (chatResponse as CancelResponse).message || '',
Expand Down Expand Up @@ -422,8 +440,7 @@ export class InlineChatHandler extends Disposable {
isRetry,
});

let answer = (chatResponse as ReplyResponse).message;
answer = this.formatAnswer(answer, crossCode);
const answer = this.formatAnswer((chatResponse as ReplyResponse).message, crossCode);

this.aiInlineChatOperationDisposed.addDispose(
this.diffPreviewer.onReady(() => {
Expand All @@ -436,11 +453,19 @@ export class InlineChatHandler extends Disposable {

this.aiInlineChatOperationDisposed.addDispose(
this.diffPreviewer.onDispose(() => {
this.aiInlineContentWidget.dispose();
this.aiInlineContentWidget?.dispose();
}),
);
}

acceptAllPartialEdits() {
this.diffPreviewer.handleAction(EResultKind.ACCEPT);
}

discardAllPartialEdits() {
this.diffPreviewer.handleAction(EResultKind.DISCARD);
}

private async handleDiffPreviewStrategy(
monacoEditor: monaco.ICodeEditor,
strategy: (...arg: any[]) => MaybePromise<ChatResponse | InlineChatController>,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Autowired, INJECTOR_TOKEN, Injectable, Injector } from '@opensumi/di';
import { Disposable, ErrorResponse, ReplyResponse } from '@opensumi/ide-core-common';
import { Disposable, ErrorResponse, IDisposable, ReplyResponse } from '@opensumi/ide-core-common';
import { EOL, ICodeEditor, IPosition, ITextModel, Position, Selection } from '@opensumi/ide-monaco';
import { ContentWidgetPositionPreference } from '@opensumi/ide-monaco/lib/browser/monaco-exports/editor';
import { DefaultEndOfLine } from '@opensumi/monaco-editor-core/esm/vs/editor/common/model';
Expand Down Expand Up @@ -172,7 +172,7 @@ export class LiveInlineDiffPreviewer extends BaseInlineDiffPreviewer<InlineStrea
return Position.lift({ lineNumber: Math.max(0, zone.startLineNumber - 1), column: 1 });
}
setValue(content: string): void {
const diffModel = this.node.recompute(EComputerMode.legacy, content);
const diffModel = this.node.recompute(EComputerMode.default, content);
this.node.readyRender(diffModel);
}
handleAction(action: EResultKind): void {
Expand Down Expand Up @@ -203,4 +203,7 @@ export class LiveInlineDiffPreviewer extends BaseInlineDiffPreviewer<InlineStrea
const diffModel = this.node.recompute(EComputerMode.legacy);
this.node.readyRender(diffModel);
}
get onPartialEditEvent() {
return this.node.onPartialEditEvent;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export class InlineInputHandler extends Disposable {
if (InlineChatController.is(previewResponse)) {
const controller = previewResponse as InlineChatController;

controller.deffered.resolve();
controller.deferred.resolve();

inputDisposable.addDispose([
controller.onData(async (data) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export class InlineStreamDiffHandler extends Disposable {
private undoRedoGroup: UndoRedoGroup;
private partialEditWidgetHandle: (widgets: AcceptPartialEditWidget[]) => void;

protected readonly _onDidEditChange = new Emitter<void>();
protected readonly _onDidEditChange = this.registerDispose(new Emitter<void>());
public readonly onDidEditChange: Event<void> = this._onDidEditChange.event;

constructor(private readonly monacoEditor: ICodeEditor, private readonly selection: Selection) {
Expand Down Expand Up @@ -385,8 +385,7 @@ export class InlineStreamDiffHandler extends Disposable {
}

public addLinesToDiff(newText: string, computerMode: EComputerMode = EComputerMode.default): void {
this.virtualModel.setValue(newText);
this.recompute(computerMode);
this.recompute(computerMode, newText);
this.doSchedulerEdits();
}

Expand All @@ -397,4 +396,8 @@ export class InlineStreamDiffHandler extends Disposable {
this.pushStackElement();
this.monacoEditor.focus();
}

get onPartialEditEvent() {
return this.livePreviewDiffDecorationModel.onPartialEditEvent;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import ReactDOMClient from 'react-dom/client';
import { Autowired, INJECTOR_TOKEN, Injectable, Injector } from '@opensumi/di';
import { KeybindingRegistry, StackingLevel } from '@opensumi/ide-core-browser';
import { AI_INLINE_DIFF_PARTIAL_EDIT } from '@opensumi/ide-core-browser/lib/ai-native/command';
import { Disposable, Emitter, Event, isUndefined, uuid } from '@opensumi/ide-core-common';
import { Disposable, Emitter, Event, IRange, isUndefined, uuid } from '@opensumi/ide-core-common';
import { ISingleEditOperation } from '@opensumi/ide-editor';
import { ICodeEditor, IEditorDecorationsCollection, Position, Range, Selection } from '@opensumi/ide-monaco';
import { ReactInlineContentWidget } from '@opensumi/ide-monaco/lib/browser/ai-native/BaseInlineContentWidget';
Expand Down Expand Up @@ -46,6 +46,31 @@ enum EPartialEdit {
discard = 'discard',
}

export interface IPartialEditEvent {
/**
* 总 diff 数
*/
totalPartialEditCount: number;
/**
* 已采纳数
*/
acceptedPartialEditCount: number;

/**
* 已添加行数
*/
totalAddedLinesCount: number;
/**
* 已删除行数
*/
totalRemovedLinesCount: number;
currentPartialEdit: {
type: EPartialEdit;
deletedLinesCount: number;
addedLinesCount: number;
};
}

interface ITextLinesTokens {
text: string;
lineTokens: LineTokens;
Expand All @@ -64,10 +89,10 @@ export class AcceptPartialEditWidget extends ReactInlineContentWidget {
private _id: string;
private _decorationId: string;

private readonly _onAccept = new Emitter<void>();
private readonly _onAccept = this.registerDispose(new Emitter<void>());
public readonly onAccept: Event<void> = this._onAccept.event;

private readonly _onDiscard = new Emitter<void>();
private readonly _onDiscard = this.registerDispose(new Emitter<void>());
public readonly onDiscard: Event<void> = this._onDiscard.event;

positionPreference = [ContentWidgetPositionPreference.EXACT];
Expand Down Expand Up @@ -191,6 +216,9 @@ export class LivePreviewDiffDecorationModel extends Disposable {
@Autowired(InlineStreamDiffService)
private readonly inlineStreamDiffService: InlineStreamDiffService;

private readonly _onPartialEditEvent = this.registerDispose(new Emitter<IPartialEditEvent>());
public readonly onPartialEditEvent: Event<IPartialEditEvent> = this._onPartialEditEvent.event;

private zoneDec: IEditorDecorationsCollection;

private activeLineDec: IEditorDecorationsCollection;
Expand All @@ -205,7 +233,7 @@ export class LivePreviewDiffDecorationModel extends Disposable {
private zone: LineRange;
private aiNativeContextKey: AINativeContextKey;

protected readonly _onPartialEditWidgetListChange = new Emitter<AcceptPartialEditWidget[]>();
protected readonly _onPartialEditWidgetListChange = this.registerDispose(new Emitter<AcceptPartialEditWidget[]>());
public readonly onPartialEditWidgetListChange: Event<AcceptPartialEditWidget[]> =
this._onPartialEditWidgetListChange.event;

Expand Down Expand Up @@ -417,19 +445,19 @@ export class LivePreviewDiffDecorationModel extends Disposable {
/**
* added widget 通常是在 removed widget 的下面一行的位置
*/
const findRemovedWidget = this.removedZoneWidgets.find((w) => w.position?.lineNumber === position.lineNumber - 1);
const findAddedDec = this.addedRangeDec.getDecorationByLineNumber(position.lineNumber);
const removedWidget = this.removedZoneWidgets.find((w) => w.position?.lineNumber === position.lineNumber - 1);
const addedDec = this.addedRangeDec.getDecorationByLineNumber(position.lineNumber);

const hide = () => {
widget.hide();
findAddedDec?.hide();
findRemovedWidget?.hide();
addedDec?.hide();
removedWidget?.hide();
};

const resume = () => {
widget.resume();
findAddedDec?.resume();
findRemovedWidget?.resume();
addedDec?.resume();
removedWidget?.resume();
};

/**
Expand All @@ -451,7 +479,7 @@ export class LivePreviewDiffDecorationModel extends Disposable {

case EPartialEdit.discard:
{
const operation = this.doDiscardPartialWidget(widget, findAddedDec, findRemovedWidget);
const operation = this.doDiscardPartialWidget(widget, addedDec, removedWidget);
if (operation) {
if (isPushStack) {
this.pushUndoElement({
Expand All @@ -469,7 +497,24 @@ export class LivePreviewDiffDecorationModel extends Disposable {
break;
}

const event: IPartialEditEvent = {
totalPartialEditCount: this.partialEditWidgetList.length,
acceptedPartialEditCount: this.partialEditWidgetList.filter((w) => w.isHidden).length,
totalAddedLinesCount: this.addedRangeDec.getDecorations().reduce((prev, current) => prev + current.length, 0),
totalRemovedLinesCount: this.removedZoneWidgets.reduce(
(prev, current) => prev + current.getRemovedTextLines().length,
0,
),
currentPartialEdit: {
addedLinesCount: addedDec?.length || 0,
deletedLinesCount: removedWidget?.getRemovedTextLines().length || 0,
type,
},
};

this.monacoEditor.focus();

this._onPartialEditEvent.fire(event);
this._onPartialEditWidgetListChange.fire(this.partialEditWidgetList);
}

Expand All @@ -489,8 +534,8 @@ export class LivePreviewDiffDecorationModel extends Disposable {
}

public discardUnProcessed(): void {
const otherWidgets = this.partialEditWidgetList.filter((widget) => !widget.isHidden);
otherWidgets.forEach((widget) => {
const showingWidgets = this.partialEditWidgetList.filter((widget) => !widget.isHidden);
showingWidgets.forEach((widget) => {
this.handlePartialEditAction(EPartialEdit.discard, widget, false);
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,10 @@ export abstract class ReactInlineContentWidget extends Disposable implements IIn
}

resume(): void {
this._isHidden = false;
this.editor.addContentWidget(this);
if (this._isHidden) {
this._isHidden = false;
this.editor.addContentWidget(this);
}
}

getId(): string {
Expand Down
Loading