@opensumi/ide-ai-native 3.7.1-next-1739761524.0 → 3.7.1-next-1739774311.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 (52) hide show
  1. package/lib/browser/ai-core.contribution.js +1 -1
  2. package/lib/browser/ai-core.contribution.js.map +1 -1
  3. package/lib/browser/components/utils.d.ts +2 -2
  4. package/lib/browser/contrib/intelligent-completions/index.d.ts +13 -12
  5. package/lib/browser/contrib/intelligent-completions/index.d.ts.map +1 -1
  6. package/lib/browser/contrib/intelligent-completions/index.js +6 -1
  7. package/lib/browser/contrib/intelligent-completions/index.js.map +1 -1
  8. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.d.ts +2 -3
  9. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.d.ts.map +1 -1
  10. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.js +39 -40
  11. package/lib/browser/contrib/intelligent-completions/intelligent-completions.controller.js.map +1 -1
  12. package/lib/browser/contrib/intelligent-completions/source/base.d.ts +8 -1
  13. package/lib/browser/contrib/intelligent-completions/source/base.d.ts.map +1 -1
  14. package/lib/browser/contrib/intelligent-completions/source/base.js +21 -3
  15. package/lib/browser/contrib/intelligent-completions/source/base.js.map +1 -1
  16. package/lib/browser/contrib/intelligent-completions/source/line-change.source.d.ts +10 -2
  17. package/lib/browser/contrib/intelligent-completions/source/line-change.source.d.ts.map +1 -1
  18. package/lib/browser/contrib/intelligent-completions/source/line-change.source.js +78 -35
  19. package/lib/browser/contrib/intelligent-completions/source/line-change.source.js.map +1 -1
  20. package/lib/browser/contrib/intelligent-completions/source/lint-error.source.d.ts +1 -3
  21. package/lib/browser/contrib/intelligent-completions/source/lint-error.source.d.ts.map +1 -1
  22. package/lib/browser/contrib/intelligent-completions/source/lint-error.source.js +13 -20
  23. package/lib/browser/contrib/intelligent-completions/source/lint-error.source.js.map +1 -1
  24. package/lib/browser/contrib/intelligent-completions/source/typing.source.d.ts +2 -2
  25. package/lib/browser/contrib/intelligent-completions/source/typing.source.d.ts.map +1 -1
  26. package/lib/browser/contrib/intelligent-completions/source/typing.source.js +6 -8
  27. package/lib/browser/contrib/intelligent-completions/source/typing.source.js.map +1 -1
  28. package/lib/browser/types.d.ts +5 -1
  29. package/lib/browser/types.d.ts.map +1 -1
  30. package/lib/browser/types.js.map +1 -1
  31. package/lib/browser/widget/inline-chat/inline-chat-editor.controller.js +7 -7
  32. package/lib/browser/widget/inline-chat/inline-chat-editor.controller.js.map +1 -1
  33. package/lib/browser/widget/inline-diff/inline-diff.controller.js +1 -1
  34. package/lib/browser/widget/inline-diff/inline-diff.controller.js.map +1 -1
  35. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.d.ts.map +1 -1
  36. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.js +5 -7
  37. package/lib/browser/widget/inline-stream-diff/inline-stream-diff.handler.js.map +1 -1
  38. package/lib/common/utils.js +2 -2
  39. package/lib/common/utils.js.map +1 -1
  40. package/package.json +21 -21
  41. package/src/browser/ai-core.contribution.ts +1 -1
  42. package/src/browser/contrib/intelligent-completions/index.ts +15 -4
  43. package/src/browser/contrib/intelligent-completions/intelligent-completions.controller.ts +71 -53
  44. package/src/browser/contrib/intelligent-completions/source/base.ts +28 -5
  45. package/src/browser/contrib/intelligent-completions/source/line-change.source.ts +111 -59
  46. package/src/browser/contrib/intelligent-completions/source/lint-error.source.ts +19 -31
  47. package/src/browser/contrib/intelligent-completions/source/typing.source.ts +7 -9
  48. package/src/browser/types.ts +3 -1
  49. package/src/browser/widget/inline-chat/inline-chat-editor.controller.ts +8 -8
  50. package/src/browser/widget/inline-diff/inline-diff.controller.ts +1 -1
  51. package/src/browser/widget/inline-stream-diff/inline-stream-diff.handler.tsx +5 -7
  52. package/src/common/utils.ts +2 -2
@@ -3,78 +3,104 @@ import { AINativeSettingSectionsId, ECodeEditsSourceTyping, IDisposable } from '
3
3
  import { ICursorPositionChangedEvent, IModelContentChangedEvent } from '@opensumi/ide-monaco';
4
4
  import {
5
5
  autorunDelta,
6
+ derived,
6
7
  derivedHandleChanges,
7
8
  observableFromEvent,
8
- recomputeInitiallyAndOnChange,
9
+ onObservableChange,
9
10
  } from '@opensumi/ide-monaco/lib/common/observable';
10
11
 
12
+ import { IntelligentCompletionsController } from '../intelligent-completions.controller';
13
+
11
14
  import { BaseCodeEditsSource } from './base';
12
15
 
13
16
  export interface ILineChangeData {
14
17
  currentLineNumber: number;
15
18
  preLineNumber?: number;
16
- change?: IModelContentChangedEvent;
17
19
  }
18
20
 
21
+ const DEPRECATED_LIMIT = 5;
22
+ const CONTENT_CHANGE_VALID_TIME = 60 * 1000;
23
+
19
24
  @Injectable({ multiple: true })
20
25
  export class LineChangeCodeEditsSource extends BaseCodeEditsSource {
21
- public priority = 2;
26
+ public priority = 1;
22
27
 
23
- public mount(): IDisposable {
24
- const modelContentChangeObs = observableFromEvent<IModelContentChangedEvent>(
25
- this,
26
- this.monacoEditor.onDidChangeModelContent,
27
- (event: IModelContentChangedEvent) => event,
28
- );
29
- const positionChangeObs = observableFromEvent<ICursorPositionChangedEvent>(
30
- this,
31
- this.monacoEditor.onDidChangeCursorPosition,
32
- (event: ICursorPositionChangedEvent) => event,
33
- );
28
+ /**
29
+ * 在当前文件,计算弃用上次 edit 时的次数是否超过了阈值 {@link DEPRECATED_LIMIT} 次,超过则不会触发
30
+ * 1. 直接 esc 弃用
31
+ * 2. 用户再次移动光标位置致使补全消失也视为弃用
32
+ */
33
+ private readonly deprecatedStore = new Map<string, number>();
34
34
 
35
- const latestModelContentChangeObs = derivedHandleChanges(
36
- {
37
- owner: this,
38
- createEmptyChangeSummary: () => ({ change: undefined }),
39
- handleChange: (ctx, changeSummary: { change: IModelContentChangedEvent | undefined }) => {
40
- // 如果只是改了光标则设置 change 为空,避免获取到缓存的 change
41
- if (ctx.didChange(positionChangeObs)) {
42
- changeSummary.change = undefined;
43
- } else {
44
- changeSummary.change = modelContentChangeObs.get();
45
- }
46
- return true;
47
- },
48
- },
49
- (reader, changeSummary) => {
50
- positionChangeObs.read(reader);
51
- modelContentChangeObs.read(reader);
52
- return changeSummary.change;
35
+ private readonly positionChangeObs = observableFromEvent<ICursorPositionChangedEvent>(
36
+ this,
37
+ this.monacoEditor.onDidChangeCursorPosition,
38
+ (event: ICursorPositionChangedEvent) => event,
39
+ );
40
+
41
+ private readonly contentChangeObs = observableFromEvent<IModelContentChangedEvent>(
42
+ this,
43
+ this.monacoEditor.onDidChangeModelContent,
44
+ (event: IModelContentChangedEvent) => event,
45
+ );
46
+
47
+ private readonly latestContentChangeTimeObs = derivedHandleChanges(
48
+ {
49
+ owner: this,
50
+ createEmptyChangeSummary: () => ({ latestContentChangeTime: 0 }),
51
+ handleChange: (context, changeSummary) => {
52
+ if (context.didChange(this.contentChangeObs)) {
53
+ changeSummary.latestContentChangeTime = Date.now();
54
+ }
55
+ return true;
53
56
  },
54
- );
57
+ },
58
+ (reader, changeSummary) => {
59
+ this.contentChangeObs.read(reader);
60
+ return changeSummary.latestContentChangeTime;
61
+ },
62
+ );
55
63
 
56
- this.addDispose(recomputeInitiallyAndOnChange(latestModelContentChangeObs));
64
+ private readonly isAllowTriggerObs = derived((reader) => {
65
+ this.positionChangeObs.read(reader);
66
+ const latestContentChangeTime = this.latestContentChangeTimeObs.read(reader);
57
67
 
58
- let lastModelContent: IModelContentChangedEvent | undefined;
59
- this.addDispose(
60
- /**
61
- * 由于 monaco 的 changeModelContent 事件比 changeCursorPosition 事件先触发,所以这里需要拿上一次的值进行消费
62
- * 否则永远返回 undefined
63
- */
64
- autorunDelta(latestModelContentChangeObs, ({ lastValue }) => {
65
- lastModelContent = lastValue;
66
- }),
67
- );
68
+ const isLineChangeEnabled = this.preferenceService.getValid(AINativeSettingSectionsId.CodeEditsLineChange, false);
69
+
70
+ /**
71
+ * 配置开关
72
+ */
73
+ if (!isLineChangeEnabled) {
74
+ return false;
75
+ }
76
+
77
+ /**
78
+ * 弃用次数规则的限制
79
+ */
80
+ const deprecatedCount = this.deprecatedStore.get(this.model?.id || '');
81
+ if (deprecatedCount && deprecatedCount >= DEPRECATED_LIMIT) {
82
+ return false;
83
+ }
84
+
85
+ /**
86
+ * 1. 未编辑过代码不触发
87
+ * 2. 编辑过代码后,60 内没有再次编辑也不触发
88
+ */
89
+ if (
90
+ latestContentChangeTime === 0 ||
91
+ (latestContentChangeTime && Date.now() - latestContentChangeTime > CONTENT_CHANGE_VALID_TIME)
92
+ ) {
93
+ return false;
94
+ }
68
95
 
96
+ return true;
97
+ });
98
+
99
+ public mount(): IDisposable {
69
100
  this.addDispose(
70
- autorunDelta(positionChangeObs, ({ lastValue, newValue }) => {
71
- const contentChange = lastModelContent;
72
-
73
- const isLineChangeEnabled = this.preferenceService.getValid(
74
- AINativeSettingSectionsId.CodeEditsLineChange,
75
- false,
76
- );
77
- if (!isLineChangeEnabled) {
101
+ autorunDelta(this.positionChangeObs, ({ lastValue, newValue }, reader) => {
102
+ const isAllowTriggerObs = this.isAllowTriggerObs.read(reader);
103
+ if (!isAllowTriggerObs) {
78
104
  return false;
79
105
  }
80
106
 
@@ -83,20 +109,46 @@ export class LineChangeCodeEditsSource extends BaseCodeEditsSource {
83
109
  if (prePosition && prePosition.lineNumber !== currentPosition?.lineNumber) {
84
110
  this.setBean({
85
111
  typing: ECodeEditsSourceTyping.LineChange,
86
- position: currentPosition,
87
112
  data: {
88
- preLineNumber: prePosition.lineNumber,
89
- currentLineNumber: currentPosition.lineNumber,
90
- change: contentChange,
113
+ [ECodeEditsSourceTyping.LineChange]: {
114
+ preLineNumber: prePosition.lineNumber,
115
+ currentLineNumber: currentPosition.lineNumber,
116
+ },
91
117
  },
92
118
  });
93
119
  }
94
-
95
- // 消费完之后设置为 undefined,避免下次获取到缓存的值
96
- lastModelContent = undefined;
97
120
  }),
98
121
  );
99
122
 
123
+ const discard = IntelligentCompletionsController.get(this.monacoEditor)?.discard;
124
+ const accept = IntelligentCompletionsController.get(this.monacoEditor)?.accept;
125
+ if (discard) {
126
+ this.addDispose(
127
+ onObservableChange(discard, (isValid: boolean) => {
128
+ const modelId = this.model?.id;
129
+ if (!modelId || !isValid) {
130
+ return;
131
+ }
132
+
133
+ const count = this.deprecatedStore.get(modelId) || 0;
134
+ this.deprecatedStore.set(modelId, count + 1);
135
+ }),
136
+ );
137
+ }
138
+
139
+ if (accept) {
140
+ this.addDispose(
141
+ onObservableChange(accept, () => {
142
+ const modelId = this.model?.id;
143
+ if (!modelId) {
144
+ return;
145
+ }
146
+
147
+ this.deprecatedStore.delete(modelId);
148
+ }),
149
+ );
150
+ }
151
+
100
152
  return this;
101
153
  }
102
154
  }
@@ -1,14 +1,8 @@
1
- import { Autowired, Injectable } from '@opensumi/di';
2
- import {
3
- AINativeSettingSectionsId,
4
- ECodeEditsSourceTyping,
5
- Event,
6
- FRAME_THREE,
7
- IDisposable,
8
- } from '@opensumi/ide-core-common';
1
+ import { Injectable } from '@opensumi/di';
2
+ import { AINativeSettingSectionsId, ECodeEditsSourceTyping, IDisposable } from '@opensumi/ide-core-common';
9
3
  import { ICursorPositionChangedEvent, IPosition, Position } from '@opensumi/ide-monaco';
10
4
  import { URI } from '@opensumi/ide-monaco/lib/browser/monaco-api';
11
- import { IWorkspaceService } from '@opensumi/ide-workspace';
5
+ import { autorunDelta, observableFromEvent } from '@opensumi/ide-monaco/lib/common/observable';
12
6
  import { StandaloneServices } from '@opensumi/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices';
13
7
  import {
14
8
  IMarker,
@@ -20,7 +14,6 @@ import {
20
14
  import { BaseCodeEditsSource } from './base';
21
15
 
22
16
  export interface ILinterErrorData {
23
- relativeWorkspacePath: string;
24
17
  errors: Array<IMarkerErrorData>;
25
18
  }
26
19
 
@@ -58,22 +51,19 @@ namespace MarkerErrorData {
58
51
 
59
52
  @Injectable({ multiple: true })
60
53
  export class LintErrorCodeEditsSource extends BaseCodeEditsSource {
61
- public priority = 1;
62
-
63
- @Autowired(IWorkspaceService)
64
- private readonly workspaceService: IWorkspaceService;
54
+ public priority = 0;
65
55
 
66
56
  public mount(): IDisposable {
67
- let prePosition = this.monacoEditor.getPosition();
57
+ const positionChangeObs = observableFromEvent<ICursorPositionChangedEvent>(
58
+ this,
59
+ this.monacoEditor.onDidChangeCursorPosition,
60
+ (event: ICursorPositionChangedEvent) => event,
61
+ );
68
62
 
69
63
  this.addDispose(
70
- // 仅在光标的行号发生变化时,才触发
71
- Event.debounce(
72
- this.monacoEditor.onDidChangeCursorPosition,
73
- (_, e) => e,
74
- FRAME_THREE,
75
- )(async (event: ICursorPositionChangedEvent) => {
76
- const currentPosition = event.position;
64
+ autorunDelta(positionChangeObs, ({ lastValue, newValue }) => {
65
+ const prePosition = lastValue?.position;
66
+ const currentPosition = newValue?.position;
77
67
 
78
68
  // 如果是 selection 则不触发
79
69
  const selection = this.monacoEditor.getSelection();
@@ -81,16 +71,16 @@ export class LintErrorCodeEditsSource extends BaseCodeEditsSource {
81
71
  return;
82
72
  }
83
73
 
84
- if (prePosition && prePosition.lineNumber !== currentPosition.lineNumber) {
85
- await this.doTrigger(currentPosition);
74
+ // 仅在光标的行号发生变化时,才触发
75
+ if (prePosition && prePosition.lineNumber !== currentPosition?.lineNumber) {
76
+ this.doTrigger(currentPosition);
86
77
  }
87
- prePosition = currentPosition;
88
78
  }),
89
79
  );
90
80
  return this;
91
81
  }
92
82
 
93
- protected async doTrigger(position: Position) {
83
+ protected doTrigger(position: Position) {
94
84
  const isLintErrorsEnabled = this.preferenceService.getValid(AINativeSettingSectionsId.CodeEditsLintErrors, false);
95
85
 
96
86
  if (!isLintErrorsEnabled || !this.model) {
@@ -104,14 +94,12 @@ export class LintErrorCodeEditsSource extends BaseCodeEditsSource {
104
94
  markers = markers.filter((marker) => Math.abs(marker.startLineNumber - position.lineNumber) <= 1);
105
95
 
106
96
  if (markers.length) {
107
- const relativeWorkspacePath = await this.workspaceService.asRelativePath(resource.path);
108
-
109
97
  this.setBean({
110
98
  typing: ECodeEditsSourceTyping.LinterErrors,
111
- position,
112
99
  data: {
113
- relativeWorkspacePath: relativeWorkspacePath?.path ?? resource.path,
114
- errors: markers.map((marker) => MarkerErrorData.toData(marker)),
100
+ [ECodeEditsSourceTyping.LinterErrors]: {
101
+ errors: markers.map((marker) => MarkerErrorData.toData(marker)),
102
+ },
115
103
  },
116
104
  });
117
105
  }
@@ -1,26 +1,23 @@
1
1
  import { Injectable } from '@opensumi/di';
2
2
  import { AINativeSettingSectionsId, ECodeEditsSourceTyping, IDisposable } from '@opensumi/ide-core-common';
3
- import { IModelContentChangedEvent, Position } from '@opensumi/ide-monaco';
3
+ import { IModelContentChangedEvent } from '@opensumi/ide-monaco';
4
4
 
5
5
  import { BaseCodeEditsSource } from './base';
6
6
 
7
7
  @Injectable({ multiple: true })
8
8
  export class TypingCodeEditsSource extends BaseCodeEditsSource {
9
- public priority = 0;
9
+ public priority = 2;
10
10
 
11
11
  public mount(): IDisposable {
12
12
  this.addDispose(
13
13
  this.monacoEditor.onDidChangeModelContent((event: IModelContentChangedEvent) => {
14
- const position = this.monacoEditor.getPosition();
15
- if (position) {
16
- this.doTrigger(position, event);
17
- }
14
+ this.doTrigger(event);
18
15
  }),
19
16
  );
20
17
  return this;
21
18
  }
22
19
 
23
- protected async doTrigger(position: Position, data: IModelContentChangedEvent) {
20
+ protected async doTrigger(data: IModelContentChangedEvent) {
24
21
  const isTypingEnabled = this.preferenceService.getValid(AINativeSettingSectionsId.CodeEditsTyping, false);
25
22
 
26
23
  if (!isTypingEnabled || !this.model) {
@@ -29,8 +26,9 @@ export class TypingCodeEditsSource extends BaseCodeEditsSource {
29
26
 
30
27
  this.setBean({
31
28
  typing: ECodeEditsSourceTyping.Typing,
32
- position,
33
- data,
29
+ data: {
30
+ [ECodeEditsSourceTyping.Typing]: data,
31
+ },
34
32
  });
35
33
  }
36
34
  }
@@ -42,7 +42,9 @@ interface IBaseInlineChatHandler<T extends any[]> {
42
42
  providerDiffPreviewStrategy?: (...args: T) => MaybePromise<ChatResponse | InlineChatController>;
43
43
  }
44
44
 
45
- export type IEditorInlineChatHandler = IBaseInlineChatHandler<[editor: ICodeEditor, token: CancellationToken]>;
45
+ export type IEditorInlineChatHandler = IBaseInlineChatHandler<
46
+ [editor: ICodeEditor, selection: ISelection, token: CancellationToken]
47
+ >;
46
48
  export type IInteractiveInputHandler = IBaseInlineChatHandler<
47
49
  [editor: ICodeEditor, selection: ISelection, value: string, token: CancellationToken]
48
50
  >;
@@ -280,7 +280,7 @@ export class InlineChatEditorController extends BaseAIMonacoEditorController {
280
280
  return undefined;
281
281
  }
282
282
 
283
- return strategy.bind(this, monacoEditor, this.token);
283
+ return strategy.bind(this, monacoEditor, crossSelection, this.token);
284
284
  };
285
285
 
286
286
  this.runAction({
@@ -297,7 +297,7 @@ export class InlineChatEditorController extends BaseAIMonacoEditorController {
297
297
  });
298
298
  return relationId;
299
299
  },
300
- execute: handler.execute ? handler.execute!.bind(this, monacoEditor, this.token) : undefined,
300
+ execute: handler.execute ? handler.execute!.bind(this, monacoEditor, crossSelection, this.token) : undefined,
301
301
  providerPreview: previewer(),
302
302
  extraData: {
303
303
  actionSource: source === 'codeAction' ? ActionSourceEnum.CodeAction : ActionSourceEnum.InlineChat,
@@ -508,12 +508,6 @@ export class InlineChatEditorController extends BaseAIMonacoEditorController {
508
508
  return;
509
509
  }
510
510
 
511
- this.visibleDiffWidget({
512
- monacoEditor,
513
- options: { crossSelection, chatResponse: response },
514
- reportInfo: { relationId, startTime, isRetry, actionType, actionSource },
515
- });
516
-
517
511
  this.aiInlineChatOperationDisposable.addDispose([
518
512
  this.aiInlineContentWidget.onResultClick((kind: EResultKind) => {
519
513
  const modifyContent = this.inlineDiffController.getModifyContent();
@@ -579,6 +573,12 @@ export class InlineChatEditorController extends BaseAIMonacoEditorController {
579
573
  });
580
574
  }),
581
575
  ]);
576
+
577
+ this.visibleDiffWidget({
578
+ monacoEditor,
579
+ options: { crossSelection, chatResponse: response },
580
+ reportInfo: { relationId, startTime, isRetry, actionType, actionSource },
581
+ });
582
582
  }
583
583
 
584
584
  public async runAction(params: {
@@ -74,7 +74,7 @@ export class InlineDiffController extends BaseAIMonacoEditorController {
74
74
  * 保留 previewer 的实例,仅卸载 previewer 的渲染层
75
75
  */
76
76
  const id = model.id;
77
- const previewer = this.currentPreviewer.read(reader);
77
+ const previewer = this.currentPreviewer.get();
78
78
  if (previewer && previewer.modelId !== id && !previewer.disposed) {
79
79
  previewer.hide();
80
80
  }
@@ -467,17 +467,15 @@ export class InlineStreamDiffHandler extends Disposable implements IInlineDiffPr
467
467
  }
468
468
 
469
469
  public pushRateFinallyDiffStack(diffModel: IComputeDiffData): void {
470
- transaction((tx) => {
471
- this.finallyDiffModel.set(diffModel, tx);
472
- // 可能存在 rate editor controller 处理完之后接口层流式才结束
473
- if (this.isEditing === false) {
474
- this.finallyRender(diffModel);
475
- }
476
- });
470
+ // 可能存在 rate editor controller 处理完之后接口层流式才结束
471
+ if (this.isEditing === false) {
472
+ this.finallyRender(diffModel);
473
+ }
477
474
  }
478
475
 
479
476
  public finallyRender(diffModel: IComputeDiffData): void {
480
477
  transaction((tx) => {
478
+ this.finallyDiffModel.set(diffModel, tx);
481
479
  this.diffModel.set(diffModel, tx);
482
480
  });
483
481
 
@@ -32,10 +32,10 @@ export const extractCodeBlocks = (content: string): string => {
32
32
  let startLine = 0;
33
33
 
34
34
  lines.forEach((line, i) => {
35
- if (!inBlock && line.startsWith(BACK_QUOTE_3_SYMBOL)) {
35
+ if (!inBlock && line.trim().startsWith(BACK_QUOTE_3_SYMBOL)) {
36
36
  inBlock = true;
37
37
  startLine = i + 1;
38
- } else if (inBlock && line.startsWith(BACK_QUOTE_3_SYMBOL)) {
38
+ } else if (inBlock && line.trim().startsWith(BACK_QUOTE_3_SYMBOL)) {
39
39
  inBlock = false;
40
40
  const endLine = i;
41
41
  newContents = lines.slice(startLine, endLine);