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 all 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 @@ -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
Loading