Skip to content

Commit

Permalink
feat: optimize inline diff viewer (#3836)
Browse files Browse the repository at this point in the history
Co-authored-by: qingyi.xjh <[email protected]>
  • Loading branch information
bytemain and Ricbet committed Jul 11, 2024
1 parent 99398fa commit 5ece2fb
Show file tree
Hide file tree
Showing 14 changed files with 282 additions and 154 deletions.
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 @@ -20,7 +20,7 @@ export interface InlineChatControllerOptions {
enableCodeblockRender: boolean;
}
export class InlineChatController {
static is(controller: any): boolean {
static is(controller: any): controller is InlineChatController {
return controller instanceof InlineChatController && typeof controller.mountReadable === 'function';
}

Expand All @@ -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
155 changes: 52 additions & 103 deletions packages/ai-native/src/browser/widget/inline-chat/inline-chat.handler.ts
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 @@ -16,7 +17,6 @@ import {
ILoggerManagerClient,
InlineChatFeatureRegistryToken,
MaybePromise,
ReplyResponse,
SupportLogNamespace,
runWhenIdle,
} from '@opensumi/ide-core-common';
Expand All @@ -25,17 +25,12 @@ import { IEditor } from '@opensumi/ide-editor/lib/browser';
import { WorkbenchEditorServiceImpl } from '@opensumi/ide-editor/lib/browser/workbench-editor.service';
import * as monaco from '@opensumi/ide-monaco';
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 { ERunStrategy } from '../../types';
import {
BaseInlineDiffPreviewer,
LiveInlineDiffPreviewer,
SideBySideInlineDiffWidget,
} from '../inline-diff/inline-diff-previewer';
import { BaseInlineDiffPreviewer } from '../inline-diff/inline-diff-previewer';
import { InlineDiffWidget } from '../inline-diff/inline-diff-widget';
import { InlineDiffService } from '../inline-diff/inline-diff.service';
import { InlineStreamDiffHandler } from '../inline-stream-diff/inline-stream-diff.handler';

import { InlineChatController } from './inline-chat-controller';
Expand Down Expand Up @@ -72,12 +67,15 @@ export class InlineChatHandler extends Disposable {
@Autowired(CodeActionService)
private readonly codeActionService: CodeActionService;

@Autowired(InlineDiffService)
private readonly inlineDiffService: InlineDiffService;

private logger: ILogServiceClient;

private diffPreviewer: BaseInlineDiffPreviewer<InlineDiffWidget | InlineStreamDiffHandler>;
private aiInlineContentWidget: AIInlineContentWidget;
private aiInlineChatDisposed: Disposable = new Disposable();
private aiInlineChatOperationDisposed: Disposable = new Disposable();
private aiInlineChatDisposable: Disposable = new Disposable();
private aiInlineChatOperationDisposable: Disposable = new Disposable();
private cancelIndicator = new CancellationTokenSource();

constructor() {
Expand All @@ -95,8 +93,8 @@ export class InlineChatHandler extends Disposable {
[
this.diffPreviewer,
this.aiInlineContentWidget,
this.aiInlineChatDisposed,
this.aiInlineChatOperationDisposed,
this.aiInlineChatDisposable,
this.aiInlineChatOperationDisposable,
].forEach((widget) => {
widget?.dispose();
});
Expand Down Expand Up @@ -207,21 +205,20 @@ export class InlineChatHandler extends Disposable {
const selection = monacoEditor.getSelection();

if (!selection || selection.isEmpty()) {
this.disposeAllWidget();
return;
}

this.aiInlineContentWidget = this.injector.get(AIInlineContentWidget, [monacoEditor]);

this.aiInlineContentWidget.show({ selection });

this.aiInlineChatDisposed.addDispose(
this.aiInlineChatDisposable.addDispose(
this.inlineChatFeatureRegistry.onChatClick(() => {
this.aiInlineChatService.launchInputVisible(true);
}),
);

this.aiInlineChatDisposed.addDispose(
this.aiInlineChatDisposable.addDispose(
this.aiInlineContentWidget.onActionClick(({ actionId, source }) => {
const handler = this.inlineChatFeatureRegistry.getEditorHandler(actionId);
const action = this.inlineChatFeatureRegistry.getAction(actionId);
Expand All @@ -248,7 +245,7 @@ export class InlineChatHandler extends Disposable {
}),
);

this.aiInlineChatDisposed.addDispose(
this.aiInlineChatDisposable.addDispose(
this.aiInlineContentWidget.onInteractiveInputValue(async (value) => {
const handler = this.inlineChatFeatureRegistry.getInteractiveInputHandler();

Expand Down Expand Up @@ -298,9 +295,13 @@ 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));
this.aiInlineChatDisposable.addDispose(this.aiInlineContentWidget.launchChatStatus(status));
this.aiReporter.end(relationId, {
message,
success: status !== EInlineChatStatus.ERROR,
Expand All @@ -322,79 +323,44 @@ export class InlineChatHandler extends Disposable {
isRetry: boolean;
},
): void {
const { crossSelection, chatResponse } = options;
const { chatResponse } = options;
const { relationId, startTime, isRetry } = reportInfo;

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

if (inlineDiffMode === EInlineDifPreviewMode.sideBySide) {
this.diffPreviewer = this.injector.get(SideBySideInlineDiffWidget, [monacoEditor, crossSelection]);
} else {
this.diffPreviewer = this.injector.get(LiveInlineDiffPreviewer, [monacoEditor, crossSelection]);
}

this.diffPreviewer = this.inlineDiffService.createDiffPreviewer(monacoEditor, options);
this.diffPreviewer.mount(this.aiInlineContentWidget);

this.diffPreviewer.show(
crossSelection.startLineNumber - 1,
crossSelection.endLineNumber - crossSelection.startLineNumber + 2,
);
this.aiInlineChatDisposable.addDispose(this.diffPreviewer);

if (InlineChatController.is(chatResponse)) {
const controller = chatResponse as InlineChatController;

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

this.aiInlineChatOperationDisposed.addDispose([
controller.onData((data) => {
if (ReplyResponse.is(data)) {
this.diffPreviewer.onData(data);
}
}),
controller.onError((error) => {
this.convertInlineChatStatus(EInlineChatStatus.ERROR, {
relationId,
message: error.message || '',
startTime,
isRetry,
});
this.diffPreviewer.onError(error);
this.diffPreviewer.layout();
}),
controller.onAbort(() => {
this.convertInlineChatStatus(EInlineChatStatus.READY, {
relationId,
message: 'abort',
startTime,
isRetry,
isStop: true,
});
this.diffPreviewer.onAbort();
this.diffPreviewer.layout();
}),
controller.onEnd(() => {
this.convertInlineChatStatus(EInlineChatStatus.DONE, {
relationId,
message: '',
startTime,
isRetry,
});
this.diffPreviewer.onEnd();
this.diffPreviewer.layout();
}),
]);
this.aiInlineChatOperationDisposable.addDispose([
chatResponse.onError((error) => {
this.convertInlineChatStatus(EInlineChatStatus.ERROR, {
relationId,
message: error.message || '',
startTime,
isRetry,
});
}),
);
chatResponse.onAbort(() => {
this.convertInlineChatStatus(EInlineChatStatus.READY, {
relationId,
message: 'abort',
startTime,
isRetry,
isStop: true,
});
}),
chatResponse.onEnd(() => {
this.convertInlineChatStatus(EInlineChatStatus.DONE, {
relationId,
message: '',
startTime,
isRetry,
});
}),
]);
} else {
const model = monacoEditor.getModel();
const crossCode = model!.getValueInRange(crossSelection);

if (this.aiInlineChatDisposed.disposed || CancelResponse.is(chatResponse)) {
if ((this.aiInlineContentWidget && this.aiInlineChatDisposable.disposed) || CancelResponse.is(chatResponse)) {
this.convertInlineChatStatus(EInlineChatStatus.READY, {
relationId,
message: (chatResponse as CancelResponse).message || '',
Expand All @@ -421,24 +387,7 @@ export class InlineChatHandler extends Disposable {
startTime,
isRetry,
});

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

this.aiInlineChatOperationDisposed.addDispose(
this.diffPreviewer.onReady(() => {
this.diffPreviewer.setValue(answer);
}),
);
}

this.diffPreviewer.layout();

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

private async handleDiffPreviewStrategy(
Expand All @@ -455,8 +404,8 @@ export class InlineChatHandler extends Disposable {
const model = monacoEditor.getModel();

this.diffPreviewer?.dispose();
this.aiInlineChatOperationDisposed.dispose();
this.aiInlineChatDisposed.addDispose(this.aiInlineContentWidget.launchChatStatus(EInlineChatStatus.THINKING));
this.aiInlineChatOperationDisposable.dispose();
this.aiInlineChatDisposable.addDispose(this.aiInlineContentWidget.launchChatStatus(EInlineChatStatus.THINKING));

const startTime = Date.now();

Expand Down Expand Up @@ -491,7 +440,7 @@ export class InlineChatHandler extends Disposable {
{ relationId, startTime, isRetry },
);

this.aiInlineChatOperationDisposed.addDispose([
this.aiInlineChatOperationDisposable.addDispose([
this.aiInlineContentWidget.onResultClick((kind: EResultKind) => {
if (kind === EResultKind.ACCEPT) {
this.diffPreviewer.handleAction(kind);
Expand Down Expand Up @@ -549,7 +498,7 @@ export class InlineChatHandler extends Disposable {
false,
);

this.aiInlineChatDisposed.addDispose([
this.aiInlineChatDisposable.addDispose([
this.aiInlineContentWidget.onResultClick(async (kind: EResultKind) => {
this.diffPreviewer.handleAction(kind);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,7 @@ import { Injectable } from '@opensumi/di';
import { IAIInlineChatService } from '@opensumi/ide-core-browser';
import { Emitter, Event, runWhenIdle } from '@opensumi/ide-core-common';

export const enum EInlineChatStatus {
READY,
THINKING,
DONE,
ERROR,
}

export const enum EResultKind {
ACCEPT = 'ACCEPT',
DISCARD = 'DISCARD',
REGENERATE = 'REGENERATE',
}
export { EInlineChatStatus, EResultKind } from '../../../common';

@Injectable({ multiple: false })
export class AIInlineChatService implements IAIInlineChatService {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ export class AIInlineContentWidget extends ReactInlineContentWidget {
constructor(protected readonly editor: IMonacoCodeEditor) {
super(editor);

this.aiNativeContextKey = this.injector.get(AINativeContextKey, [(this.editor as any)._contextKeyService]);
this.aiNativeContextKey = this.injector.get(AINativeContextKey, [this.editor.contextKeyService]);
this.addDispose(
this.editor.onDidLayoutChange(() => {
if (this.isOutOfArea()) {
Expand Down
3 changes: 3 additions & 0 deletions packages/ai-native/src/browser/widget/inline-diff/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './inline-diff.service';
export * from './inline-diff-previewer';
export * from './inline-diff-widget';
Loading

0 comments on commit 5ece2fb

Please sign in to comment.