@opensumi/ide-ai-native 3.7.2-next-1740450374.0 → 3.8.1-next-1740452092.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/lib/browser/ai-core.contribution.d.ts.map +1 -1
  2. package/lib/browser/ai-core.contribution.js +4 -0
  3. package/lib/browser/ai-core.contribution.js.map +1 -1
  4. package/lib/browser/chat/chat.module.less +2 -2
  5. package/lib/browser/chat/chat.view.d.ts.map +1 -1
  6. package/lib/browser/chat/chat.view.js +14 -19
  7. package/lib/browser/chat/chat.view.js.map +1 -1
  8. package/lib/browser/components/chat-history.css +1 -1
  9. package/lib/browser/components/components.module.less +2 -2
  10. package/lib/browser/contrib/intelligent-completions/index.d.ts +12 -7
  11. package/lib/browser/contrib/intelligent-completions/index.d.ts.map +1 -1
  12. package/lib/browser/contrib/intelligent-completions/index.js +13 -2
  13. package/lib/browser/contrib/intelligent-completions/index.js.map +1 -1
  14. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.d.ts +2 -7
  15. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.d.ts.map +1 -1
  16. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.js +6 -106
  17. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.js.map +1 -1
  18. package/lib/browser/contrib/intelligent-completions/view/base.d.ts +18 -0
  19. package/lib/browser/contrib/intelligent-completions/view/base.d.ts.map +1 -0
  20. package/lib/browser/contrib/intelligent-completions/view/base.js +18 -0
  21. package/lib/browser/contrib/intelligent-completions/view/base.js.map +1 -0
  22. package/lib/browser/contrib/intelligent-completions/view/code-edits-previewer.d.ts +19 -0
  23. package/lib/browser/contrib/intelligent-completions/view/code-edits-previewer.d.ts.map +1 -0
  24. package/lib/browser/contrib/intelligent-completions/view/code-edits-previewer.js +67 -0
  25. package/lib/browser/contrib/intelligent-completions/view/code-edits-previewer.js.map +1 -0
  26. package/lib/browser/contrib/intelligent-completions/view/default.d.ts +11 -0
  27. package/lib/browser/contrib/intelligent-completions/view/default.d.ts.map +1 -0
  28. package/lib/browser/contrib/intelligent-completions/view/default.js +104 -0
  29. package/lib/browser/contrib/intelligent-completions/view/default.js.map +1 -0
  30. package/lib/browser/contrib/intelligent-completions/view/legacy.d.ts +16 -0
  31. package/lib/browser/contrib/intelligent-completions/view/legacy.d.ts.map +1 -0
  32. package/lib/browser/contrib/intelligent-completions/view/legacy.js +126 -0
  33. package/lib/browser/contrib/intelligent-completions/view/legacy.js.map +1 -0
  34. package/lib/browser/layout/layout.module.less +1 -1
  35. package/lib/browser/mcp/tools/components/index.module.less +4 -0
  36. package/lib/browser/preferences/schema.d.ts.map +1 -1
  37. package/lib/browser/preferences/schema.js +7 -0
  38. package/lib/browser/preferences/schema.js.map +1 -1
  39. package/lib/browser/widget/inline-hint/inline-hint.controller.d.ts.map +1 -1
  40. package/lib/browser/widget/inline-hint/inline-hint.controller.js +4 -2
  41. package/lib/browser/widget/inline-hint/inline-hint.controller.js.map +1 -1
  42. package/package.json +23 -23
  43. package/src/browser/ai-core.contribution.ts +4 -0
  44. package/src/browser/chat/chat.module.less +2 -2
  45. package/src/browser/chat/chat.view.tsx +29 -30
  46. package/src/browser/components/chat-history.css +1 -1
  47. package/src/browser/components/components.module.less +2 -2
  48. package/src/browser/contrib/intelligent-completions/index.ts +27 -8
  49. package/src/browser/contrib/intelligent-completions/intelligent-completions.controller.ts +10 -165
  50. package/src/browser/contrib/intelligent-completions/view/base.ts +31 -0
  51. package/src/browser/contrib/intelligent-completions/view/code-edits-previewer.ts +69 -0
  52. package/src/browser/contrib/intelligent-completions/view/default.ts +146 -0
  53. package/src/browser/contrib/intelligent-completions/view/legacy.ts +182 -0
  54. package/src/browser/layout/layout.module.less +1 -1
  55. package/src/browser/mcp/tools/components/index.module.less +4 -0
  56. package/src/browser/preferences/schema.ts +8 -0
  57. package/src/browser/widget/inline-hint/inline-hint.controller.ts +5 -1
@@ -16,7 +16,7 @@ import {
16
16
  IntelligentCompletionsRegistryToken,
17
17
  runWhenIdle,
18
18
  } from '@opensumi/ide-core-common';
19
- import { Emitter, ICodeEditor, ICursorPositionChangedEvent, IRange, ITextModel, Range } from '@opensumi/ide-monaco';
19
+ import { Emitter, ICodeEditor, ICursorPositionChangedEvent, ITextModel } from '@opensumi/ide-monaco';
20
20
  import {
21
21
  IObservable,
22
22
  ISettableObservable,
@@ -30,7 +30,6 @@ import {
30
30
  observableValue,
31
31
  transaction,
32
32
  } from '@opensumi/ide-monaco/lib/common/observable';
33
- import { empty } from '@opensumi/ide-utils/lib/strings';
34
33
  import { EditorContextKeys } from '@opensumi/monaco-editor-core/esm/vs/editor/common/editorContextKeys';
35
34
  import { inlineSuggestCommitId } from '@opensumi/monaco-editor-core/esm/vs/editor/contrib/inlineCompletions/browser/controller/commandIds';
36
35
  import { InlineCompletionContextKeys } from '@opensumi/monaco-editor-core/esm/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionContextKeys';
@@ -42,23 +41,15 @@ import {
42
41
  import { ContextKeyExpr } from '@opensumi/monaco-editor-core/esm/vs/platform/contextkey/common/contextkey';
43
42
 
44
43
  import { AINativeContextKey } from '../../ai-core.contextkeys';
45
- import { REWRITE_DECORATION_INLINE_ADD, RewriteWidget } from '../../widget/rewrite/rewrite-widget';
46
44
  import { BaseAIMonacoEditorController } from '../base';
47
45
 
48
- import { AdditionsDeletionsDecorationModel } from './decoration/additions-deletions.decoration';
49
- import { MultiLineDecorationModel } from './decoration/multi-line.decoration';
50
- import {
51
- IMultiLineDiffChangeResult,
52
- computeMultiLineDiffChanges,
53
- mergeMultiLineDiffChanges,
54
- wordChangesToLineChangesMap,
55
- } from './diff-computer';
56
46
  import { IntelligentCompletionsRegistry } from './intelligent-completions.feature.registry';
57
47
  import { CodeEditsSourceCollection } from './source/base';
58
48
  import { LineChangeCodeEditsSource } from './source/line-change.source';
59
49
  import { LintErrorCodeEditsSource } from './source/lint-error.source';
60
50
  import { TriggerCodeEditsSource } from './source/trigger.source';
61
51
  import { TypingCodeEditsSource } from './source/typing.source';
52
+ import { CodeEditsPreviewer } from './view/code-edits-previewer';
62
53
 
63
54
  import { CodeEditsResultValue, VALID_TIME } from './index';
64
55
 
@@ -90,20 +81,18 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
90
81
  }
91
82
 
92
83
  private codeEditsResult: ISettableObservable<CodeEditsResultValue | undefined>;
93
- private multiLineDecorationModel: MultiLineDecorationModel;
94
- private additionsDeletionsDecorationModel: AdditionsDeletionsDecorationModel;
84
+ private multiLineEditsIsVisibleObs: IObservable<boolean>;
85
+
95
86
  private codeEditsSourceCollection: CodeEditsSourceCollection;
96
87
  private aiNativeContextKey: AINativeContextKey;
97
- private rewriteWidget: RewriteWidget | null;
98
- private multiLineEditsIsVisibleObs: IObservable<boolean>;
88
+ private codeEditsPreviewer: CodeEditsPreviewer;
99
89
 
100
90
  public mount(): IDisposable {
101
91
  this.handlerAlwaysVisiblePreference();
102
92
 
103
93
  this.codeEditsResult = observableValue<CodeEditsResultValue | undefined>(this, undefined);
104
- this.multiLineDecorationModel = new MultiLineDecorationModel(this.monacoEditor);
105
- this.additionsDeletionsDecorationModel = new AdditionsDeletionsDecorationModel(this.monacoEditor);
106
94
  this.aiNativeContextKey = this.injector.get(AINativeContextKey, [this.monacoEditor.contextKeyService]);
95
+ this.codeEditsPreviewer = this.injector.get(CodeEditsPreviewer, [this.monacoEditor, this.aiNativeContextKey]);
107
96
  this.codeEditsSourceCollection = this.injector.get(CodeEditsSourceCollection, [
108
97
  [LintErrorCodeEditsSource, LineChangeCodeEditsSource, TypingCodeEditsSource, TriggerCodeEditsSource],
109
98
  this.monacoEditor,
@@ -214,125 +203,10 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
214
203
  );
215
204
  }
216
205
 
217
- private destroyRewriteWidget() {
218
- if (this.rewriteWidget) {
219
- this.rewriteWidget.dispose();
220
- this.rewriteWidget = null;
221
- }
222
- }
223
-
224
- private applyInlineDecorations(completionModel: CodeEditsResultValue) {
225
- const { items } = completionModel;
226
- const { range, insertText } = items[0];
227
-
228
- // code edits 必须提供 range
229
- if (!range) {
230
- return;
231
- }
232
-
233
- const position = this.monacoEditor.getPosition()!;
234
- const model = this.monacoEditor.getModel();
235
- const insertTextString = insertText.toString();
236
- const originalContent = model?.getValueInRange(range);
237
- const eol = this.model.getEOL();
238
-
239
- const changes = computeMultiLineDiffChanges(
240
- originalContent!,
241
- insertTextString,
242
- this.monacoEditor,
243
- range.startLineNumber,
244
- eol,
245
- );
246
-
247
- if (!changes) {
248
- return;
249
- }
250
-
251
- const { singleLineCharChanges, charChanges, wordChanges, isOnlyAddingToEachWord } = changes;
252
-
253
- // 限制 changes 数量,超过这个数量直接显示智能重写
254
- const maxCharChanges = 20;
255
- const maxWordChanges = 20;
256
-
257
- if (
258
- range &&
259
- isOnlyAddingToEachWord &&
260
- charChanges.length <= maxCharChanges &&
261
- wordChanges.length <= maxWordChanges
262
- ) {
263
- const modificationsResult = this.multiLineDecorationModel.applyInlineDecorations(
264
- this.monacoEditor,
265
- mergeMultiLineDiffChanges(singleLineCharChanges, eol),
266
- range.startLineNumber,
267
- position,
268
- );
269
-
270
- this.aiNativeContextKey.multiLineEditsIsVisible.reset();
271
- this.multiLineDecorationModel.clearDecorations();
272
-
273
- if (!modificationsResult) {
274
- this.renderRewriteWidget(wordChanges, model, range, insertTextString);
275
- } else if (modificationsResult && modificationsResult.inlineMods) {
276
- this.aiNativeContextKey.multiLineEditsIsVisible.set(true);
277
- this.multiLineDecorationModel.updateLineModificationDecorations(modificationsResult.inlineMods);
278
- }
279
- } else {
280
- this.additionsDeletionsDecorationModel.updateDeletionsDecoration(wordChanges, range, eol);
281
- this.renderRewriteWidget(wordChanges, model, range, insertTextString);
282
- }
283
- }
284
-
285
- private async renderRewriteWidget(
286
- wordChanges: IMultiLineDiffChangeResult[],
287
- model: ITextModel | null,
288
- range: IRange,
289
- insertTextString: string,
290
- ) {
291
- this.destroyRewriteWidget();
292
-
293
- const cursorPosition = this.monacoEditor.getPosition();
294
- if (!cursorPosition) {
295
- return;
296
- }
297
-
298
- this.rewriteWidget = this.injector.get(RewriteWidget, [this.monacoEditor]);
299
-
300
- const startOffset = this.model.getOffsetAt({ lineNumber: range.startLineNumber, column: range.startColumn });
301
- const endOffset = this.model.getOffsetAt({ lineNumber: range.endLineNumber, column: range.endColumn });
302
- const allText = this.model.getValue();
303
- // 这里是为了能在 rewrite widget 的 editor 当中完整的复用代码高亮与语法检测的能力
304
- const newVirtualContent = allText.substring(0, startOffset) + insertTextString + allText.substring(endOffset);
305
-
306
- const lineChangesMap = wordChangesToLineChangesMap(wordChanges, range, model);
307
-
308
- await this.rewriteWidget.defered.promise;
309
-
310
- this.aiNativeContextKey.multiLineEditsIsVisible.set(true);
311
-
312
- const allLineChanges = Object.values(lineChangesMap).map((lineChanges) => ({
313
- changes: lineChanges
314
- .map((change) => change.filter((item) => item.value.trim() !== empty))
315
- .filter((change) => change.length > 0),
316
- }));
317
-
318
- this.rewriteWidget.setInsertText(insertTextString);
319
- this.rewriteWidget.show({ position: cursorPosition });
320
- this.rewriteWidget.setEditArea(range);
321
-
322
- if (allLineChanges.every(({ changes }) => changes.every((change) => change.every(({ removed }) => removed)))) {
323
- // 处理全是删除的情况
324
- this.rewriteWidget.renderTextLineThrough(allLineChanges);
325
- } else {
326
- this.rewriteWidget.renderVirtualEditor(newVirtualContent, wordChanges);
327
- }
328
- }
329
-
330
206
  public hide() {
331
207
  this.cancelToken();
332
208
  this.aiNativeContextKey.multiLineEditsIsVisible.reset();
333
- this.multiLineDecorationModel.clearDecorations();
334
- this.additionsDeletionsDecorationModel.clearDeletionsDecorations();
335
- this.destroyRewriteWidget();
209
+ this.codeEditsPreviewer.hide();
336
210
  }
337
211
 
338
212
  private readonly reportData = derived(this, (reader) => {
@@ -382,6 +256,7 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
382
256
  report?.('isValid', false);
383
257
  }
384
258
 
259
+ this.codeEditsPreviewer.discard();
385
260
  this.hide();
386
261
  return isValid;
387
262
  },
@@ -391,27 +266,7 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
391
266
  const report = this.reportData.read(reader);
392
267
  report?.('isReceive');
393
268
 
394
- this.multiLineDecorationModel.accept();
395
-
396
- if (this.rewriteWidget) {
397
- this.rewriteWidget.accept();
398
-
399
- const virtualEditor = this.rewriteWidget.getVirtualEditor();
400
- // 采纳完之后将 virtualEditor 的 decorations 重新映射在 editor 上
401
- if (virtualEditor) {
402
- const editArea = this.rewriteWidget.getEditArea();
403
- const decorations = virtualEditor.getDecorationsInRange(Range.lift(editArea));
404
- const preAddedDecorations = decorations?.filter(
405
- (decoration) => decoration.options.description === REWRITE_DECORATION_INLINE_ADD,
406
- );
407
- if (preAddedDecorations) {
408
- this.additionsDeletionsDecorationModel.updateAdditionsDecoration(
409
- preAddedDecorations.map((decoration) => decoration.range),
410
- );
411
- }
412
- }
413
- }
414
-
269
+ this.codeEditsPreviewer.accept();
415
270
  this.hide();
416
271
  });
417
272
 
@@ -423,16 +278,6 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
423
278
  }
424
279
 
425
280
  private registerFeature(monacoEditor: ICodeEditor): void {
426
- this.featureDisposable.addDispose(
427
- Event.any<any>(
428
- monacoEditor.onDidChangeCursorPosition,
429
- monacoEditor.onDidChangeModelContent,
430
- monacoEditor.onDidBlurEditorWidget,
431
- )(() => {
432
- this.additionsDeletionsDecorationModel.clearAdditionsDecorations();
433
- }),
434
- );
435
-
436
281
  // 监听当前光标位置的变化,如果超出 range 区域则表示弃用
437
282
  this.featureDisposable.addDispose(
438
283
  this.monacoEditor.onDidChangeCursorPosition((event: ICursorPositionChangedEvent) => {
@@ -505,7 +350,7 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
505
350
  }
506
351
 
507
352
  try {
508
- this.applyInlineDecorations(completionModel);
353
+ this.codeEditsPreviewer.render(completionModel);
509
354
  } catch (error) {
510
355
  this.logger.warn('IntelligentCompletionsController applyInlineDecorations error', error);
511
356
  }
@@ -0,0 +1,31 @@
1
+ import { Injector } from '@opensumi/di';
2
+ import { Disposable } from '@opensumi/ide-core-common';
3
+ import { ICodeEditor } from '@opensumi/ide-monaco';
4
+ import {
5
+ ObservableCodeEditor,
6
+ observableCodeEditor,
7
+ } from '@opensumi/monaco-editor-core/esm/vs/editor/browser/observableCodeEditor';
8
+
9
+ import { CodeEditsResultValue } from '../index';
10
+
11
+ export abstract class BaseCodeEditsView extends Disposable {
12
+ protected editorObs: ObservableCodeEditor;
13
+
14
+ public modelId: string;
15
+
16
+ constructor(protected readonly monacoEditor: ICodeEditor, protected readonly injector: Injector) {
17
+ super();
18
+
19
+ this.editorObs = observableCodeEditor(this.monacoEditor);
20
+ this.mount();
21
+
22
+ this.addDispose({ dispose: () => this.hide() });
23
+ }
24
+
25
+ protected mount(): void {}
26
+
27
+ abstract render(completionModel: CodeEditsResultValue): void;
28
+ abstract hide(): void;
29
+ abstract accept(): void;
30
+ abstract discard(): void;
31
+ }
@@ -0,0 +1,69 @@
1
+ import { Autowired, INJECTOR_TOKEN, Injectable, Injector, Optional } from '@opensumi/di';
2
+ import { PreferenceService } from '@opensumi/ide-core-browser';
3
+ import { AINativeSettingSectionsId, Disposable } from '@opensumi/ide-core-common';
4
+ import { ICodeEditor } from '@opensumi/ide-monaco';
5
+ import { IObservable, IReader, autorun, observableFromEvent } from '@opensumi/ide-monaco/lib/common/observable';
6
+
7
+ import { AINativeContextKey } from '../../../ai-core.contextkeys';
8
+ import { CodeEditsRenderType, CodeEditsResultValue } from '../index';
9
+
10
+ import { BaseCodeEditsView } from './base';
11
+ import { DefaultCodeEditsView } from './default';
12
+ import { LegacyCodeEditsView } from './legacy';
13
+
14
+ @Injectable()
15
+ export class CodeEditsPreviewer extends Disposable {
16
+ @Autowired(INJECTOR_TOKEN)
17
+ protected readonly injector: Injector;
18
+
19
+ @Autowired(PreferenceService)
20
+ private readonly preferenceService: PreferenceService;
21
+
22
+ private readonly renderTypeObs: IObservable<CodeEditsRenderType>;
23
+ private view: BaseCodeEditsView | undefined;
24
+
25
+ constructor(
26
+ @Optional() protected readonly monacoEditor: ICodeEditor,
27
+ @Optional() protected readonly aiNativeContextKey: AINativeContextKey,
28
+ ) {
29
+ super();
30
+
31
+ this.renderTypeObs = observableFromEvent(this, this.preferenceService.onPreferenceChanged, () =>
32
+ this.preferenceService.getValid(AINativeSettingSectionsId.CodeEditsRenderType, CodeEditsRenderType.Default),
33
+ );
34
+
35
+ this.addDispose(
36
+ autorun((reader: IReader) => {
37
+ const renderType = this.renderTypeObs.read(reader);
38
+ if (this.view) {
39
+ this.view.dispose();
40
+ this.view = undefined;
41
+ }
42
+
43
+ if (renderType === CodeEditsRenderType.Default) {
44
+ this.view = new DefaultCodeEditsView(this.monacoEditor, this.injector);
45
+ } else {
46
+ this.view = new LegacyCodeEditsView(this.monacoEditor, this.injector);
47
+ }
48
+ }),
49
+ );
50
+ }
51
+
52
+ public render(completionModel: CodeEditsResultValue) {
53
+ this.view?.render(completionModel);
54
+ this.aiNativeContextKey.multiLineEditsIsVisible.set(true);
55
+ }
56
+
57
+ public hide() {
58
+ this.view?.hide();
59
+ this.aiNativeContextKey.multiLineEditsIsVisible.set(false);
60
+ }
61
+
62
+ public accept() {
63
+ this.view?.accept();
64
+ }
65
+
66
+ public discard() {
67
+ this.view?.discard();
68
+ }
69
+ }
@@ -0,0 +1,146 @@
1
+ import {
2
+ InlineCompletionContext,
3
+ InlineCompletionTriggerKind,
4
+ InlineCompletionsProvider,
5
+ Position,
6
+ Range,
7
+ } from '@opensumi/ide-monaco';
8
+ import {
9
+ IObservable,
10
+ asyncTransaction,
11
+ constObservable,
12
+ derived,
13
+ transaction,
14
+ } from '@opensumi/ide-monaco/lib/common/observable';
15
+ import { equalsIfDefined, itemEquals } from '@opensumi/monaco-editor-core/esm/vs/base/common/equals';
16
+ import { LanguageFeatureRegistry } from '@opensumi/monaco-editor-core/esm/vs/editor/common/languageFeatureRegistry';
17
+ import { InlineCompletionsController } from '@opensumi/monaco-editor-core/esm/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController';
18
+ import { InlineCompletionsModel } from '@opensumi/monaco-editor-core/esm/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel';
19
+ import {
20
+ InlineCompletionsSource,
21
+ UpToDateInlineCompletions,
22
+ } from '@opensumi/monaco-editor-core/esm/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource';
23
+ import {
24
+ InlineCompletionProviderResult,
25
+ provideInlineCompletions,
26
+ } from '@opensumi/monaco-editor-core/esm/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions';
27
+
28
+ import { CodeEditsResultValue, ICodeEdit, ICodeEditsResult } from '../index';
29
+
30
+ import { BaseCodeEditsView } from './base';
31
+
32
+ /**
33
+ * copy from @opensumi/monaco-editor-core/esm/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions
34
+ */
35
+ class UpdateRequest {
36
+ constructor(
37
+ public readonly position: Position,
38
+ public readonly context: InlineCompletionContext,
39
+ public readonly versionId: number,
40
+ ) {}
41
+
42
+ public satisfies(other: UpdateRequest): boolean {
43
+ return (
44
+ this.position.equals(other.position) &&
45
+ equalsIfDefined(this.context.selectedSuggestionInfo, other.context.selectedSuggestionInfo, itemEquals()) &&
46
+ (other.context.triggerKind === InlineCompletionTriggerKind.Automatic ||
47
+ this.context.triggerKind === InlineCompletionTriggerKind.Explicit) &&
48
+ this.versionId === other.versionId
49
+ );
50
+ }
51
+
52
+ public get isExplicitRequest() {
53
+ return this.context.triggerKind === InlineCompletionTriggerKind.Explicit;
54
+ }
55
+ }
56
+
57
+ export class DefaultCodeEditsView extends BaseCodeEditsView {
58
+ private monacoCompletionsModel: IObservable<InlineCompletionsModel | undefined> = derived(this, (reader) => {
59
+ const inlineCompletionsController = InlineCompletionsController.get(this.monacoEditor);
60
+ if (!inlineCompletionsController) {
61
+ return;
62
+ }
63
+
64
+ return inlineCompletionsController.model.read(reader);
65
+ });
66
+
67
+ private monacoCompletionsSource: IObservable<InlineCompletionsSource | undefined> = derived(this, (reader) => {
68
+ const model = this.monacoCompletionsModel.read(reader);
69
+ if (!model) {
70
+ return;
71
+ }
72
+
73
+ const source = model['_source'] as InlineCompletionsSource;
74
+ return source;
75
+ });
76
+
77
+ public render(completionModel: CodeEditsResultValue): void {
78
+ const source = this.monacoCompletionsSource.get();
79
+ if (!source) {
80
+ return;
81
+ }
82
+
83
+ asyncTransaction(async (tx) => {
84
+ const position = this.editorObs.positions.get()?.[0];
85
+ if (!position) {
86
+ return;
87
+ }
88
+
89
+ const model = this.editorObs.model.get();
90
+ if (!model) {
91
+ return;
92
+ }
93
+
94
+ const versionId = this.editorObs.versionId.get();
95
+ const context: InlineCompletionContext = {
96
+ triggerKind: InlineCompletionTriggerKind.Automatic,
97
+ selectedSuggestionInfo: undefined,
98
+ includeInlineCompletions: false,
99
+ includeInlineEdits: true,
100
+ };
101
+
102
+ const request = new UpdateRequest(position, context, versionId!);
103
+ const inlineEdits: InlineCompletionProviderResult = await provideInlineCompletions(
104
+ {
105
+ all: () => [
106
+ {
107
+ provideInlineCompletions: () => {},
108
+ provideInlineEditsForRange(model, range, context, token) {
109
+ return completionModel;
110
+ },
111
+ freeInlineCompletions: () => [],
112
+ } as InlineCompletionsProvider<ICodeEditsResult<ICodeEdit>>,
113
+ ],
114
+ } as unknown as LanguageFeatureRegistry<InlineCompletionsProvider>,
115
+ Range.lift(completionModel.range),
116
+ model,
117
+ context,
118
+ );
119
+
120
+ const completions = new UpToDateInlineCompletions(
121
+ inlineEdits,
122
+ request,
123
+ model,
124
+ this.editorObs.versionId,
125
+ constObservable(1000),
126
+ );
127
+ source.inlineCompletions.set(completions, tx);
128
+ });
129
+ }
130
+
131
+ public accept(): void {
132
+ const model = this.monacoCompletionsModel.get();
133
+ model?.accept();
134
+ }
135
+
136
+ public discard(): void {
137
+ const model = this.monacoCompletionsModel.get();
138
+ transaction((tx) => {
139
+ model?.stop('explicitCancel', tx);
140
+ });
141
+ }
142
+
143
+ public hide(): void {
144
+ this.monacoCompletionsSource.get()?.inlineCompletions?.set(undefined, undefined);
145
+ }
146
+ }
@@ -0,0 +1,182 @@
1
+ import { IRange, ITextModel, Range } from '@opensumi/ide-monaco';
2
+ import { Event } from '@opensumi/ide-utils';
3
+ import { empty } from '@opensumi/ide-utils/lib/strings';
4
+
5
+ import { REWRITE_DECORATION_INLINE_ADD, RewriteWidget } from '../../../widget/rewrite/rewrite-widget';
6
+ import { AdditionsDeletionsDecorationModel } from '../decoration/additions-deletions.decoration';
7
+ import { MultiLineDecorationModel } from '../decoration/multi-line.decoration';
8
+ import {
9
+ IMultiLineDiffChangeResult,
10
+ computeMultiLineDiffChanges,
11
+ mergeMultiLineDiffChanges,
12
+ wordChangesToLineChangesMap,
13
+ } from '../diff-computer';
14
+ import { CodeEditsResultValue } from '../index';
15
+
16
+ import { BaseCodeEditsView } from './base';
17
+
18
+ export class LegacyCodeEditsView extends BaseCodeEditsView {
19
+ private get model() {
20
+ return this.monacoEditor.getModel()!;
21
+ }
22
+
23
+ private multiLineDecorationModel: MultiLineDecorationModel = new MultiLineDecorationModel(this.monacoEditor);
24
+ private additionsDeletionsDecorationModel: AdditionsDeletionsDecorationModel = new AdditionsDeletionsDecorationModel(
25
+ this.monacoEditor,
26
+ );
27
+ private rewriteWidget: RewriteWidget | null;
28
+
29
+ protected mount(): void {
30
+ this.addDispose(
31
+ Event.any<any>(
32
+ this.monacoEditor.onDidChangeCursorPosition,
33
+ this.monacoEditor.onDidChangeModelContent,
34
+ this.monacoEditor.onDidBlurEditorWidget,
35
+ )(() => {
36
+ this.additionsDeletionsDecorationModel.clearAdditionsDecorations();
37
+ }),
38
+ );
39
+ }
40
+
41
+ private destroyRewriteWidget() {
42
+ if (this.rewriteWidget) {
43
+ this.rewriteWidget.dispose();
44
+ this.rewriteWidget = null;
45
+ }
46
+ }
47
+
48
+ private async renderRewriteWidget(
49
+ wordChanges: IMultiLineDiffChangeResult[],
50
+ model: ITextModel | null,
51
+ range: IRange,
52
+ insertTextString: string,
53
+ ) {
54
+ this.destroyRewriteWidget();
55
+
56
+ const cursorPosition = this.monacoEditor.getPosition();
57
+ if (!cursorPosition) {
58
+ return;
59
+ }
60
+
61
+ this.rewriteWidget = this.injector.get(RewriteWidget, [this.monacoEditor]);
62
+
63
+ const startOffset = this.model.getOffsetAt({ lineNumber: range.startLineNumber, column: range.startColumn });
64
+ const endOffset = this.model.getOffsetAt({ lineNumber: range.endLineNumber, column: range.endColumn });
65
+ const allText = this.model.getValue();
66
+ // 这里是为了能在 rewrite widget 的 editor 当中完整的复用代码高亮与语法检测的能力
67
+ const newVirtualContent = allText.substring(0, startOffset) + insertTextString + allText.substring(endOffset);
68
+
69
+ const lineChangesMap = wordChangesToLineChangesMap(wordChanges, range, model);
70
+
71
+ await this.rewriteWidget.defered.promise;
72
+
73
+ const allLineChanges = Object.values(lineChangesMap).map((lineChanges) => ({
74
+ changes: lineChanges
75
+ .map((change) => change.filter((item) => item.value.trim() !== empty))
76
+ .filter((change) => change.length > 0),
77
+ }));
78
+
79
+ this.rewriteWidget.setInsertText(insertTextString);
80
+ this.rewriteWidget.show({ position: cursorPosition });
81
+ this.rewriteWidget.setEditArea(range);
82
+
83
+ if (allLineChanges.every(({ changes }) => changes.every((change) => change.every(({ removed }) => removed)))) {
84
+ // 处理全是删除的情况
85
+ this.rewriteWidget.renderTextLineThrough(allLineChanges);
86
+ } else {
87
+ this.rewriteWidget.renderVirtualEditor(newVirtualContent, wordChanges);
88
+ }
89
+ }
90
+
91
+ public render(completionModel: CodeEditsResultValue): void {
92
+ const { items } = completionModel;
93
+ const { range, insertText } = items[0];
94
+
95
+ // code edits 必须提供 range
96
+ if (!range) {
97
+ return;
98
+ }
99
+
100
+ const position = this.monacoEditor.getPosition()!;
101
+ const model = this.monacoEditor.getModel();
102
+ const insertTextString = insertText.toString();
103
+ const originalContent = model?.getValueInRange(range);
104
+ const eol = this.model.getEOL();
105
+
106
+ const changes = computeMultiLineDiffChanges(
107
+ originalContent!,
108
+ insertTextString,
109
+ this.monacoEditor,
110
+ range.startLineNumber,
111
+ eol,
112
+ );
113
+
114
+ if (!changes) {
115
+ return;
116
+ }
117
+
118
+ const { singleLineCharChanges, charChanges, wordChanges, isOnlyAddingToEachWord } = changes;
119
+
120
+ // 限制 changes 数量,超过这个数量直接显示智能重写
121
+ const maxCharChanges = 20;
122
+ const maxWordChanges = 20;
123
+
124
+ if (
125
+ range &&
126
+ isOnlyAddingToEachWord &&
127
+ charChanges.length <= maxCharChanges &&
128
+ wordChanges.length <= maxWordChanges
129
+ ) {
130
+ const modificationsResult = this.multiLineDecorationModel.applyInlineDecorations(
131
+ this.monacoEditor,
132
+ mergeMultiLineDiffChanges(singleLineCharChanges, eol),
133
+ range.startLineNumber,
134
+ position,
135
+ );
136
+
137
+ this.multiLineDecorationModel.clearDecorations();
138
+
139
+ if (!modificationsResult) {
140
+ this.renderRewriteWidget(wordChanges, model, range, insertTextString);
141
+ } else if (modificationsResult && modificationsResult.inlineMods) {
142
+ this.multiLineDecorationModel.updateLineModificationDecorations(modificationsResult.inlineMods);
143
+ }
144
+ } else {
145
+ this.additionsDeletionsDecorationModel.updateDeletionsDecoration(wordChanges, range, eol);
146
+ this.renderRewriteWidget(wordChanges, model, range, insertTextString);
147
+ }
148
+ }
149
+
150
+ public accept(): void {
151
+ this.multiLineDecorationModel.accept();
152
+
153
+ if (this.rewriteWidget) {
154
+ this.rewriteWidget.accept();
155
+
156
+ const virtualEditor = this.rewriteWidget.getVirtualEditor();
157
+ // 采纳完之后将 virtualEditor 的 decorations 重新映射在 editor 上
158
+ if (virtualEditor) {
159
+ const editArea = this.rewriteWidget.getEditArea();
160
+ const decorations = virtualEditor.getDecorationsInRange(Range.lift(editArea));
161
+ const preAddedDecorations = decorations?.filter(
162
+ (decoration) => decoration.options.description === REWRITE_DECORATION_INLINE_ADD,
163
+ );
164
+ if (preAddedDecorations) {
165
+ this.additionsDeletionsDecorationModel.updateAdditionsDecoration(
166
+ preAddedDecorations.map((decoration) => decoration.range),
167
+ );
168
+ }
169
+ }
170
+ }
171
+ }
172
+
173
+ public discard(): void {
174
+ this.hide();
175
+ }
176
+
177
+ public hide(): void {
178
+ this.multiLineDecorationModel.clearDecorations();
179
+ this.additionsDeletionsDecorationModel.clearDeletionsDecorations();
180
+ this.destroyRewriteWidget();
181
+ }
182
+ }
@@ -23,7 +23,7 @@
23
23
  .rce-mbox-text {
24
24
  line-height: 18px;
25
25
  width: inherit;
26
- font-size: 14px;
26
+ font-size: 12px;
27
27
  }
28
28
 
29
29
  .rce-smsg {