44 *--------------------------------------------------------------------------------------------*/
55
66import { raceTimeout } from '../../../../../base/common/async.js' ;
7+ import { CancellationTokenSource } from '../../../../../base/common/cancellation.js' ;
78import { LcsDiff , StringDiffSequence } from '../../../../../base/common/diff/diff.js' ;
89import { Disposable } from '../../../../../base/common/lifecycle.js' ;
910import { localize } from '../../../../../nls.js' ;
10- import { CommandsRegistry } from '../../../../../platform/commands/common/commands.js' ;
11+ import { CommandsRegistry , ICommandService } from '../../../../../platform/commands/common/commands.js' ;
1112import { ServicesAccessor } from '../../../../browser/editorExtensions.js' ;
1213import { IBulkEditService } from '../../../../browser/services/bulkEditService.js' ;
1314import { TextReplacement } from '../../../../common/core/edits/textEdit.js' ;
1415import { Position } from '../../../../common/core/position.js' ;
1516import { Range } from '../../../../common/core/range.js' ;
1617import { StandardTokenType } from '../../../../common/encodedTokenAttributes.js' ;
17- import { Command } from '../../../../common/languages.js' ;
18+ import { Command , type Rejection , type WorkspaceEdit } from '../../../../common/languages.js' ;
1819import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js' ;
1920import { ITextModel } from '../../../../common/model.js' ;
2021import { ILanguageFeaturesService } from '../../../../common/services/languageFeatures.js' ;
2122import { EditSources , TextModelEditSource } from '../../../../common/textModelEditSource.js' ;
22- import { hasProvider , prepareRename , rename } from '../../../rename/browser/rename.js' ;
23+ import { hasProvider , rawRename } from '../../../rename/browser/rename.js' ;
2324import { renameSymbolCommandId } from '../controller/commandIds.js' ;
2425import { InlineSuggestionItem } from './inlineSuggestionItem.js' ;
2526import { IInlineSuggestDataActionEdit } from './provideInlineCompletions.js' ;
2627
28+ enum RenameKind {
29+ no = 'no' ,
30+ yes = 'yes' ,
31+ maybe = 'maybe'
32+ }
33+
34+ namespace RenameKind {
35+ export function fromString ( value : string ) : RenameKind {
36+ switch ( value ) {
37+ case 'no' : return RenameKind . no ;
38+ case 'yes' : return RenameKind . yes ;
39+ case 'maybe' : return RenameKind . maybe ;
40+ default : return RenameKind . no ;
41+ }
42+ }
43+ }
44+
2745export type RenameEdits = {
2846 renames : { edits : TextReplacement [ ] ; position : Position ; oldName : string ; newName : string } ;
2947 others : { edits : TextReplacement [ ] } ;
@@ -203,22 +221,77 @@ export class RenameInferenceEngine {
203221 }
204222}
205223
224+ class RenameSymbolRunnable {
225+
226+ private readonly _cancellationTokenSource : CancellationTokenSource ;
227+ private readonly _promise : Promise < WorkspaceEdit & Rejection > ;
228+ private _result : WorkspaceEdit & Rejection | undefined = undefined ;
229+
230+ constructor ( languageFeaturesService : ILanguageFeaturesService , textModel : ITextModel , position : Position , newName : string , source : TextModelEditSource ) {
231+ this . _cancellationTokenSource = new CancellationTokenSource ( ) ;
232+ this . _promise = rawRename ( languageFeaturesService . renameProvider , textModel , position , newName , this . _cancellationTokenSource . token ) ;
233+ }
234+
235+ public cancel ( ) : void {
236+ this . _cancellationTokenSource . cancel ( ) ;
237+ }
238+
239+ public async getCount ( ) : Promise < number > {
240+ const result = await this . getResult ( ) ;
241+ if ( result === undefined ) {
242+ return 0 ;
243+ }
244+
245+ return result . edits . length ;
246+ }
247+
248+ public async getWorkspaceEdit ( ) : Promise < WorkspaceEdit | undefined > {
249+ return this . getResult ( ) ;
250+ }
251+
252+ private async getResult ( ) : Promise < WorkspaceEdit | undefined > {
253+ if ( this . _result === undefined ) {
254+ this . _result = await this . _promise ;
255+ }
256+ if ( this . _result . rejectReason ) {
257+ return undefined ;
258+ }
259+ return this . _result ;
260+ }
261+ }
262+
206263export class RenameSymbolProcessor extends Disposable {
207264
208265 private readonly _renameInferenceEngine = new RenameInferenceEngine ( ) ;
209266
267+ private _renameRunnable : { id : string ; runnable : RenameSymbolRunnable } | undefined ;
268+
210269 constructor (
270+ @ICommandService private readonly _commandService : ICommandService ,
211271 @ILanguageFeaturesService private readonly _languageFeaturesService : ILanguageFeaturesService ,
212272 @ILanguageConfigurationService private readonly _languageConfigurationService : ILanguageConfigurationService ,
213273 @IBulkEditService bulkEditService : IBulkEditService ,
214274 ) {
215275 super ( ) ;
216- this . _register ( CommandsRegistry . registerCommand ( renameSymbolCommandId , async ( _ : ServicesAccessor , textModel : ITextModel , position : Position , newName : string , source : TextModelEditSource ) => {
217- const result = await rename ( this . _languageFeaturesService . renameProvider , textModel , position , newName ) ;
218- if ( result . rejectReason ) {
276+ const self = this ;
277+ this . _register ( CommandsRegistry . registerCommand ( renameSymbolCommandId , async ( _ : ServicesAccessor , textModel : ITextModel , position : Position , newName : string , source : TextModelEditSource , id : string ) => {
278+ if ( self . _renameRunnable === undefined ) {
279+ return ;
280+ }
281+ let workspaceEdit : WorkspaceEdit | undefined ;
282+ if ( self . _renameRunnable . id !== id ) {
283+ self . _renameRunnable . runnable . cancel ( ) ;
284+ self . _renameRunnable = undefined ;
285+ const runnable = new RenameSymbolRunnable ( self . _languageFeaturesService , textModel , position , newName , source ) ;
286+ workspaceEdit = await runnable . getWorkspaceEdit ( ) ;
287+ } else {
288+ workspaceEdit = await self . _renameRunnable . runnable . getWorkspaceEdit ( ) ;
289+ self . _renameRunnable = undefined ;
290+ }
291+ if ( workspaceEdit === undefined ) {
219292 return ;
220293 }
221- bulkEditService . apply ( result , { reason : source } ) ;
294+ bulkEditService . apply ( workspaceEdit , { reason : source } ) ;
222295 } ) ) ;
223296 }
224297
@@ -243,9 +316,10 @@ export class RenameSymbolProcessor extends Disposable {
243316 }
244317
245318 const { oldName, newName, position, edits : renameEdits } = edits . renames ;
319+
246320 let timedOut = false ;
247- const loc = await raceTimeout ( prepareRename ( this . _languageFeaturesService . renameProvider , textModel , position ) , 1000 , ( ) => { timedOut = true ; } ) ;
248- const renamePossible = loc !== undefined && ! loc . rejectReason && loc . text === oldName ;
321+ const check = await raceTimeout < RenameKind > ( this . checkRenamePrecondition ( textModel , position , oldName , newName ) , 1000 , ( ) => { timedOut = true ; } ) ;
322+ const renamePossible = check === RenameKind . yes || check === RenameKind . maybe ;
249323
250324 suggestItem . setRenameProcessingInfo ( {
251325 createdRename : renamePossible ,
@@ -259,6 +333,7 @@ export class RenameSymbolProcessor extends Disposable {
259333 return suggestItem ;
260334 }
261335
336+ const id = suggestItem . identity . id ;
262337 const source = EditSources . inlineCompletionAccept ( {
263338 nes : suggestItem . isInlineEdit ,
264339 requestUuid : suggestItem . requestUuid ,
@@ -268,7 +343,7 @@ export class RenameSymbolProcessor extends Disposable {
268343 const command : Command = {
269344 id : renameSymbolCommandId ,
270345 title : localize ( 'rename' , "Rename" ) ,
271- arguments : [ textModel , position , newName , source ] ,
346+ arguments : [ textModel , position , newName , source , id ] ,
272347 } ;
273348 const textReplacement = renameEdits [ 0 ] ;
274349 const renameAction : IInlineSuggestDataActionEdit = {
@@ -279,6 +354,33 @@ export class RenameSymbolProcessor extends Disposable {
279354 alternativeAction : command ,
280355 uri : textModel . uri
281356 } ;
357+
358+ if ( this . _renameRunnable !== undefined ) {
359+ this . _renameRunnable . runnable . cancel ( ) ;
360+ this . _renameRunnable = undefined ;
361+ }
362+ const runnable = new RenameSymbolRunnable ( this . _languageFeaturesService , textModel , position , newName , source ) ;
363+ this . _renameRunnable = { id, runnable } ;
364+
282365 return InlineSuggestionItem . create ( suggestItem . withAction ( renameAction ) , textModel ) ;
283366 }
367+
368+ private async checkRenamePrecondition ( textModel : ITextModel , position : Position , oldName : string , newName : string ) : Promise < RenameKind > {
369+ // const result = await prepareRename(this._languageFeaturesService.renameProvider, textModel, position, CancellationToken.None);
370+ // if (result === undefined || result.rejectReason) {
371+ // return RenameKind.no;
372+ // }
373+ // return oldName === result.text ? RenameKind.yes : RenameKind.no;
374+
375+ try {
376+ const result = await this . _commandService . executeCommand < RenameKind > ( 'github.copilot.nes.prepareRename' , textModel . uri , position , oldName , newName ) ;
377+ if ( result === undefined ) {
378+ return RenameKind . no ;
379+ } else {
380+ return RenameKind . fromString ( result ) ;
381+ }
382+ } catch ( error ) {
383+ return RenameKind . no ;
384+ }
385+ }
284386}
0 commit comments