Skip to content

Commit 50f4deb

Browse files
[cherry-pick] Fix: cross-file inline edit list never disposed (handleListEndOfLifetime not fired) (#318434)
Co-authored-by: vs-code-engineering[bot] <vs-code-engineering[bot]@users.noreply.github.com>
1 parent 151c273 commit 50f4deb

2 files changed

Lines changed: 19 additions & 12 deletions

File tree

src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1203,7 +1203,8 @@ export class InlineCompletionsModel extends Disposable {
12031203
* Used for cross-file inline edits.
12041204
*/
12051205
public transplantCompletion(item: InlineSuggestionItem): void {
1206-
item.addRef();
1206+
// No explicit addRef needed: `seedWithCompletion` creates a new `InlineCompletionsState`
1207+
// which calls `addRef` on every item it holds and pairs it with `removeRef` in dispose.
12071208
transaction(tx => {
12081209
this._source.seedWithCompletion(item, tx);
12091210
this._isActive.set(true, tx);

src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,32 @@
55

66
import { assertNever } from '../../../../../base/common/assert.js';
77
import { AsyncIterableProducer } from '../../../../../base/common/async.js';
8+
import { CachedFunction } from '../../../../../base/common/cache.js';
89
import { CancellationToken, CancellationTokenSource } from '../../../../../base/common/cancellation.js';
9-
import { BugIndicatingError, onUnexpectedExternalError } from '../../../../../base/common/errors.js';
10+
import { groupByMap } from '../../../../../base/common/collections.js';
11+
import { BugIndicatingError, onUnexpectedError, onUnexpectedExternalError } from '../../../../../base/common/errors.js';
1012
import { Disposable, IDisposable } from '../../../../../base/common/lifecycle.js';
13+
import { isDefined } from '../../../../../base/common/types.js';
14+
import { URI } from '../../../../../base/common/uri.js';
1115
import { prefixedUuid } from '../../../../../base/common/uuid.js';
1216
import { ICommandService } from '../../../../../platform/commands/common/commands.js';
1317
import { ISingleEditOperation } from '../../../../common/core/editOperation.js';
1418
import { StringReplacement } from '../../../../common/core/edits/stringEdit.js';
15-
import { OffsetRange } from '../../../../common/core/ranges/offsetRange.js';
19+
import { TextReplacement } from '../../../../common/core/edits/textEdit.js';
1620
import { Position } from '../../../../common/core/position.js';
1721
import { Range } from '../../../../common/core/range.js';
18-
import { TextReplacement } from '../../../../common/core/edits/textEdit.js';
19-
import { InlineCompletionEndOfLifeReason, InlineCompletionEndOfLifeReasonKind, InlineCompletion, InlineCompletionContext, InlineCompletions, InlineCompletionsProvider, PartialAcceptInfo, InlineCompletionsDisposeReason, LifetimeSummary, ProviderId, IInlineCompletionHint, InlineCompletionTriggerKind } from '../../../../common/languages.js';
22+
import { OffsetRange } from '../../../../common/core/ranges/offsetRange.js';
23+
import { IInlineCompletionHint, InlineCompletion, InlineCompletionContext, InlineCompletionEndOfLifeReason, InlineCompletionEndOfLifeReasonKind, InlineCompletions, InlineCompletionsDisposeReason, InlineCompletionsProvider, InlineCompletionTriggerKind, LifetimeSummary, PartialAcceptInfo, ProviderId } from '../../../../common/languages.js';
2024
import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js';
2125
import { ITextModel } from '../../../../common/model.js';
2226
import { fixBracketsInLine } from '../../../../common/model/bracketPairsTextModelPart/fixBrackets.js';
27+
import { EditDeltaInfo } from '../../../../common/textModelEditSource.js';
2328
import { SnippetParser, Text } from '../../../snippet/browser/snippetParser.js';
2429
import { ErrorResult, getReadonlyEmptyArray } from '../utils.js';
25-
import { groupByMap } from '../../../../../base/common/collections.js';
26-
import { DirectedGraph } from './graph.js';
27-
import { CachedFunction } from '../../../../../base/common/cache.js';
2830
import { InlineCompletionViewData, InlineCompletionViewKind } from '../view/inlineEdits/inlineEditsViewInterface.js';
29-
import { isDefined } from '../../../../../base/common/types.js';
30-
import { inlineCompletionIsVisible } from './inlineCompletionIsVisible.js';
31-
import { EditDeltaInfo } from '../../../../common/textModelEditSource.js';
32-
import { URI } from '../../../../../base/common/uri.js';
3331
import { InlineSuggestionEditKind } from './editKind.js';
32+
import { DirectedGraph } from './graph.js';
33+
import { inlineCompletionIsVisible } from './inlineCompletionIsVisible.js';
3434
import { InlineSuggestAlternativeAction } from './InlineSuggestAlternativeAction.js';
3535

3636
export type InlineCompletionContextWithoutUuid = Omit<InlineCompletionContext, 'requestUuid'>;
@@ -659,6 +659,12 @@ export class InlineSuggestionList {
659659
item.reportEndOfLife();
660660
}
661661
this.provider.disposeInlineCompletions(this.inlineSuggestions, reason);
662+
} else if (this.refCount < 0) {
663+
// Invariant: every addRef must be paired with exactly one removeRef.
664+
// Going negative means a removeRef without a matching addRef somewhere.
665+
onUnexpectedError(new BugIndicatingError(
666+
`InlineSuggestionList (provider=${this.provider.providerId?.toString()}) refCount went negative (${this.refCount}) — more removeRef than addRef calls.`
667+
));
662668
}
663669
}
664670
}

0 commit comments

Comments
 (0)