@@ -16,7 +16,7 @@ import {
1616 IntelligentCompletionsRegistryToken ,
1717 runWhenIdle ,
1818} 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' ;
2020import {
2121 IObservable ,
2222 ISettableObservable ,
@@ -30,7 +30,6 @@ import {
3030 observableValue ,
3131 transaction ,
3232} from '@opensumi/ide-monaco/lib/common/observable' ;
33- import { empty } from '@opensumi/ide-utils/lib/strings' ;
3433import { EditorContextKeys } from '@opensumi/monaco-editor-core/esm/vs/editor/common/editorContextKeys' ;
3534import { inlineSuggestCommitId } from '@opensumi/monaco-editor-core/esm/vs/editor/contrib/inlineCompletions/browser/controller/commandIds' ;
3635import { InlineCompletionContextKeys } from '@opensumi/monaco-editor-core/esm/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionContextKeys' ;
@@ -42,23 +41,15 @@ import {
4241import { ContextKeyExpr } from '@opensumi/monaco-editor-core/esm/vs/platform/contextkey/common/contextkey' ;
4342
4443import { AINativeContextKey } from '../../ai-core.contextkeys' ;
45- import { REWRITE_DECORATION_INLINE_ADD , RewriteWidget } from '../../widget/rewrite/rewrite-widget' ;
4644import { BaseAIMonacoEditorController } from '../base' ;
4745
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' ;
5646import { IntelligentCompletionsRegistry } from './intelligent-completions.feature.registry' ;
5747import { CodeEditsSourceCollection } from './source/base' ;
5848import { LineChangeCodeEditsSource } from './source/line-change.source' ;
5949import { LintErrorCodeEditsSource } from './source/lint-error.source' ;
6050import { TriggerCodeEditsSource } from './source/trigger.source' ;
6151import { TypingCodeEditsSource } from './source/typing.source' ;
52+ import { CodeEditsPreviewer } from './view/code-edits-previewer' ;
6253
6354import { CodeEditsResultValue , VALID_TIME } from './index' ;
6455
@@ -90,20 +81,18 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
9081 }
9182
9283 private codeEditsResult : ISettableObservable < CodeEditsResultValue | undefined > ;
93- private multiLineDecorationModel : MultiLineDecorationModel ;
94- private additionsDeletionsDecorationModel : AdditionsDeletionsDecorationModel ;
84+ private multiLineEditsIsVisibleObs : IObservable < boolean > ;
85+
9586 private codeEditsSourceCollection : CodeEditsSourceCollection ;
9687 private aiNativeContextKey : AINativeContextKey ;
97- private rewriteWidget : RewriteWidget | null ;
98- private multiLineEditsIsVisibleObs : IObservable < boolean > ;
88+ private codeEditsPreviewer : CodeEditsPreviewer ;
9989
10090 public mount ( ) : IDisposable {
10191 this . handlerAlwaysVisiblePreference ( ) ;
10292
10393 this . codeEditsResult = observableValue < CodeEditsResultValue | undefined > ( this , undefined ) ;
104- this . multiLineDecorationModel = new MultiLineDecorationModel ( this . monacoEditor ) ;
105- this . additionsDeletionsDecorationModel = new AdditionsDeletionsDecorationModel ( this . monacoEditor ) ;
10694 this . aiNativeContextKey = this . injector . get ( AINativeContextKey , [ this . monacoEditor . contextKeyService ] ) ;
95+ this . codeEditsPreviewer = this . injector . get ( CodeEditsPreviewer , [ this . monacoEditor , this . aiNativeContextKey ] ) ;
10796 this . codeEditsSourceCollection = this . injector . get ( CodeEditsSourceCollection , [
10897 [ LintErrorCodeEditsSource , LineChangeCodeEditsSource , TypingCodeEditsSource , TriggerCodeEditsSource ] ,
10998 this . monacoEditor ,
@@ -214,125 +203,10 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
214203 ) ;
215204 }
216205
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-
330206 public hide ( ) {
331207 this . cancelToken ( ) ;
332208 this . aiNativeContextKey . multiLineEditsIsVisible . reset ( ) ;
333- this . multiLineDecorationModel . clearDecorations ( ) ;
334- this . additionsDeletionsDecorationModel . clearDeletionsDecorations ( ) ;
335- this . destroyRewriteWidget ( ) ;
209+ this . codeEditsPreviewer . hide ( ) ;
336210 }
337211
338212 private readonly reportData = derived ( this , ( reader ) => {
@@ -382,6 +256,7 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
382256 report ?.( 'isValid' , false ) ;
383257 }
384258
259+ this . codeEditsPreviewer . discard ( ) ;
385260 this . hide ( ) ;
386261 return isValid ;
387262 } ,
@@ -391,27 +266,7 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
391266 const report = this . reportData . read ( reader ) ;
392267 report ?.( 'isReceive' ) ;
393268
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 ( ) ;
415270 this . hide ( ) ;
416271 } ) ;
417272
@@ -423,16 +278,6 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
423278 }
424279
425280 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-
436281 // 监听当前光标位置的变化,如果超出 range 区域则表示弃用
437282 this . featureDisposable . addDispose (
438283 this . monacoEditor . onDidChangeCursorPosition ( ( event : ICursorPositionChangedEvent ) => {
@@ -505,7 +350,7 @@ export class IntelligentCompletionsController extends BaseAIMonacoEditorControll
505350 }
506351
507352 try {
508- this . applyInlineDecorations ( completionModel ) ;
353+ this . codeEditsPreviewer . render ( completionModel ) ;
509354 } catch ( error ) {
510355 this . logger . warn ( 'IntelligentCompletionsController applyInlineDecorations error' , error ) ;
511356 }
0 commit comments