Merge pull request #141146 from microsoft/hediet/inline-completions-bracket-completion

inline completions bracket completion
This commit is contained in:
Henning Dieterichs
2022-01-24 15:32:29 +01:00
committed by GitHub
16 changed files with 286 additions and 53 deletions
+6
View File
@@ -797,6 +797,12 @@ export interface InlineCompletion {
readonly range?: IRange;
readonly command?: Command;
/**
* If set to `true`, unopened closing brackets are removed and unclosed opening brackets are closed.
* Defaults to `false`.
*/
readonly completeBracketPairs?: boolean;
}
export interface InlineCompletions<TItem extends InlineCompletion = InlineCompletion> {
+5
View File
@@ -938,6 +938,11 @@ export interface ITextModel {
*/
getTokenTypeIfInsertingCharacter(lineNumber: number, column: number, character: string): StandardTokenType;
/**
* @internal
*/
tokenizeLineWithEdit(position: IPosition, length: number, newText: string): LineTokens | null;
/**
* Get the word under or besides `position`.
* @param position The position to look for a word.
@@ -5,7 +5,7 @@
import { CursorColumns } from 'vs/editor/common/core/cursorColumns';
import { ITextModel } from 'vs/editor/common/model';
import { Length, lengthAdd, lengthGetLineCount, lengthHash, lengthToObj, lengthZero } from './length';
import { Length, lengthAdd, lengthGetLineCount, lengthToObj, lengthZero } from './length';
import { SmallImmutableSet } from './smallImmutableSet';
import { OpeningBracketId } from './tokenizer';
@@ -624,17 +624,12 @@ export class TextAstNode extends ImmutableLeafAstNode {
}
export class BracketAstNode extends ImmutableLeafAstNode {
private static cacheByLength = new Map<number, BracketAstNode>();
public static create(length: Length): BracketAstNode {
const lengthKey = lengthHash(length);
const cached = BracketAstNode.cacheByLength.get(lengthKey);
if (cached) {
return cached;
}
const node = new BracketAstNode(length);
BracketAstNode.cacheByLength.set(lengthKey, node);
public static create(
length: Length,
languageId: string,
bracketIds: SmallImmutableSet<OpeningBracketId>
): BracketAstNode {
const node = new BracketAstNode(length, languageId, bracketIds);
return node;
}
@@ -646,7 +641,15 @@ export class BracketAstNode extends ImmutableLeafAstNode {
return SmallImmutableSet.getEmpty();
}
private constructor(length: Length) {
private constructor(
length: Length,
public readonly languageId: string,
/**
* In case of a opening bracket, this is the id of the opening bracket.
* In case of a closing bracket, this contains the ids of all opening brackets it can close.
*/
public readonly bracketIds: SmallImmutableSet<OpeningBracketId>
) {
super(length);
}
@@ -41,19 +41,20 @@ export class BracketTokens {
TokenKind.ClosingBracket,
info.first,
info.openingBrackets,
BracketAstNode.create(length)
BracketAstNode.create(length, configuration.languageId, info.openingBrackets)
));
}
for (const openingText of openingBrackets) {
const length = toLength(0, openingText.length);
const openingTextId = getId(configuration.languageId, openingText);
const bracketIds = SmallImmutableSet.getEmpty().add(openingTextId, identityKeyProvider);
map.set(openingText, new Token(
length,
TokenKind.OpeningBracket,
openingTextId,
SmallImmutableSet.getEmpty().add(openingTextId, identityKeyProvider),
BracketAstNode.create(length)
bracketIds,
BracketAstNode.create(length, configuration.languageId, bracketIds)
));
}
@@ -94,6 +95,15 @@ export class BracketTokens {
return this.map.get(value);
}
findClosingTokenText(openingBracketIds: SmallImmutableSet<OpeningBracketId>): string | undefined {
for (const [closingText, info] of this.map) {
if (info.bracketIds.intersects(openingBracketIds)) {
return closingText;
}
}
return undefined;
}
get isEmpty(): boolean {
return this.map.size === 0;
}
@@ -4,13 +4,12 @@
*--------------------------------------------------------------------------------------------*/
import { NotSupportedError } from 'vs/base/common/errors';
import { LineTokens } from 'vs/editor/common/tokens/lineTokens';
import { ITextModel } from 'vs/editor/common/model';
import { SmallImmutableSet } from './smallImmutableSet';
import { StandardTokenType, TokenMetadata } from 'vs/editor/common/languages';
import { IViewLineTokens } from 'vs/editor/common/tokens/lineTokens';
import { BracketAstNode, TextAstNode } from './ast';
import { BracketTokens, LanguageAgnosticBracketTokens } from './brackets';
import { lengthGetColumnCountIfZeroLineCount, Length, lengthAdd, lengthDiff, lengthToObj, lengthZero, toLength } from './length';
import { Length, lengthAdd, lengthDiff, lengthGetColumnCountIfZeroLineCount, lengthToObj, lengthZero, toLength } from './length';
import { SmallImmutableSet } from './smallImmutableSet';
export interface Tokenizer {
readonly offset: Length;
@@ -51,6 +50,13 @@ export class Token {
) { }
}
export interface ITokenizerSource {
getValue(): string;
getLineCount(): number;
getLineLength(lineNumber: number): number;
getLineTokens(lineNumber: number): IViewLineTokens;
}
export class TextBufferTokenizer implements Tokenizer {
private readonly textBufferLineCount: number;
private readonly textBufferLastLineLength: number;
@@ -58,7 +64,7 @@ export class TextBufferTokenizer implements Tokenizer {
private readonly reader = new NonPeekableTextBufferTokenizer(this.textModel, this.bracketTokens);
constructor(
private readonly textModel: ITextModel,
private readonly textModel: ITokenizerSource,
private readonly bracketTokens: LanguageAgnosticBracketTokens
) {
this.textBufferLineCount = textModel.getLineCount();
@@ -119,7 +125,7 @@ class NonPeekableTextBufferTokenizer {
private readonly textBufferLineCount: number;
private readonly textBufferLastLineLength: number;
constructor(private readonly textModel: ITextModel, private readonly bracketTokens: LanguageAgnosticBracketTokens) {
constructor(private readonly textModel: ITokenizerSource, private readonly bracketTokens: LanguageAgnosticBracketTokens) {
this.textBufferLineCount = textModel.getLineCount();
this.textBufferLastLineLength = textModel.getLineLength(this.textBufferLineCount);
}
@@ -127,7 +133,7 @@ class NonPeekableTextBufferTokenizer {
private lineIdx = 0;
private line: string | null = null;
private lineCharOffset = 0;
private lineTokens: LineTokens | null = null;
private lineTokens: IViewLineTokens | null = null;
private lineTokenOffset = 0;
public setPosition(lineIdx: number, column: number): void {
@@ -0,0 +1,82 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry';
import { AstNode, AstNodeKind } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/ast';
import { LanguageAgnosticBracketTokens } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/brackets';
import { Length, lengthAdd, lengthGetColumnCountIfZeroLineCount, lengthZero } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/length';
import { parseDocument } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/parser';
import { DenseKeyProvider } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/smallImmutableSet';
import { ITokenizerSource, TextBufferTokenizer } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/tokenizer';
import { IViewLineTokens } from 'vs/editor/common/tokens/lineTokens';
export function fixBracketsInLine(tokens: IViewLineTokens, languageConfigurationService: ILanguageConfigurationService): string {
const denseKeyProvider = new DenseKeyProvider<string>();
const bracketTokens = new LanguageAgnosticBracketTokens(denseKeyProvider, (languageId) =>
languageConfigurationService.getLanguageConfiguration(languageId)
);
const tokenizer = new TextBufferTokenizer(
new StaticTokenizerSource([tokens]),
bracketTokens
);
const node = parseDocument(tokenizer, [], undefined, true);
let str = '';
const line = tokens.getLineContent();
function processNode(node: AstNode, offset: Length) {
if (node.kind === AstNodeKind.Pair) {
processNode(node.openingBracket, offset);
offset = lengthAdd(offset, node.openingBracket.length);
if (node.child) {
processNode(node.child, offset);
offset = lengthAdd(offset, node.child.length);
}
if (node.closingBracket) {
processNode(node.closingBracket, offset);
offset = lengthAdd(offset, node.closingBracket.length);
} else {
const singleLangBracketTokens = bracketTokens.getSingleLanguageBracketTokens(node.openingBracket.languageId);
const closingTokenText = singleLangBracketTokens.findClosingTokenText(node.openingBracket.bracketIds);
str += closingTokenText;
}
} else if (node.kind === AstNodeKind.UnexpectedClosingBracket) {
// remove the bracket
} else if (node.kind === AstNodeKind.Text || node.kind === AstNodeKind.Bracket) {
str += line.substring(
lengthGetColumnCountIfZeroLineCount(offset),
lengthGetColumnCountIfZeroLineCount(lengthAdd(offset, node.length))
);
} else if (node.kind === AstNodeKind.List) {
for (const child of node.children) {
processNode(child, offset);
offset = lengthAdd(offset, child.length);
}
}
}
processNode(node, lengthZero);
return str;
}
class StaticTokenizerSource implements ITokenizerSource {
constructor(private readonly lines: IViewLineTokens[]) { }
getValue(): string {
return this.lines.map(l => l.getLineContent()).join('\n');
}
getLineCount(): number {
return this.lines.length;
}
getLineLength(lineNumber: number): number {
return this.lines[lineNumber - 1].getLineContent().length;
}
getLineTokens(lineNumber: number): IViewLineTokens {
return this.lines[lineNumber - 1];
}
}
+5
View File
@@ -2139,6 +2139,11 @@ export class TextModel extends Disposable implements model.ITextModel, IDecorati
return this._tokenization.getTokenTypeIfInsertingCharacter(position, character);
}
tokenizeLineWithEdit(position: IPosition, length: number, newText: string): LineTokens | null {
const validatedPosition = this.validatePosition(position);
return this._tokenization.tokenizeLineWithEdit(validatedPosition, length, newText);
}
private getLanguageConfiguration(languageId: string): ResolvedLanguageConfiguration {
return this._languageConfigurationService.getLanguageConfiguration(languageId);
}
@@ -372,6 +372,38 @@ export class TextModelTokenization extends Disposable {
return lineTokens.getStandardTokenType(tokenIndex);
}
public tokenizeLineWithEdit(position: Position, length: number, newText: string): LineTokens | null {
const lineNumber = position.lineNumber;
const column = position.column;
if (!this._tokenizationSupport) {
return null;
}
this.forceTokenization(lineNumber);
const lineStartState = this._tokenizationStateStore.getBeginState(lineNumber - 1);
if (!lineStartState) {
return null;
}
const curLineContent = this._textModel.getLineContent(lineNumber);
const newLineContent = curLineContent.substring(0, column - 1)
+ newText + curLineContent.substring(column - 1 + length);
const languageId = this._textModel.getLanguageIdAtPosition(lineNumber, 0);
const result = safeTokenize(
this._languageIdCodec,
languageId,
this._tokenizationSupport,
newLineContent,
true,
lineStartState
);
const lineTokens = new LineTokens(result.tokens, newLineContent, this._languageIdCodec);
return lineTokens;
}
public isCheapToTokenize(lineNumber: number): boolean {
if (!this._tokenizationSupport) {
return true;
+10
View File
@@ -15,6 +15,8 @@ export interface IViewLineTokens {
getPresentation(tokenIndex: number): ITokenPresentation;
findTokenIndexAtOffset(offset: number): number;
getLineContent(): string;
getMetadata(tokenIndex: number): number;
getLanguageId(tokenIndex: number): string;
}
export class LineTokens implements IViewLineTokens {
@@ -253,6 +255,14 @@ class SliceLineTokens implements IViewLineTokens {
}
}
public getMetadata(tokenIndex: number): number {
return this._source.getMetadata(this._firstTokenIndex + tokenIndex);
}
public getLanguageId(tokenIndex: number): string {
return this._source.getLanguageId(this._firstTokenIndex + tokenIndex);
}
public getLineContent(): string {
return this._source.getLineContent().substring(this._startOffset, this._endOffset);
}
@@ -9,8 +9,9 @@ import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { InlineCompletionTriggerKind } from 'vs/editor/common/languages';
import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry';
import { GhostText, GhostTextWidgetModel } from 'vs/editor/contrib/inlineCompletions/browser/ghostText';
import { InlineCompletionsModel, LiveInlineCompletions, SynchronizedInlineCompletionsCache } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel';
import { InlineCompletionsModel, SynchronizedInlineCompletionsCache, TrackedInlineCompletions } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel';
import { SuggestWidgetPreviewModel } from 'vs/editor/contrib/inlineCompletions/browser/suggestWidgetPreviewModel';
import { createDisposableRef } from 'vs/editor/contrib/inlineCompletions/browser/utils';
import { ICommandService } from 'vs/platform/commands/common/commands';
@@ -68,7 +69,7 @@ export abstract class DelegatingModel extends Disposable implements GhostTextWid
export class GhostTextModel extends DelegatingModel implements GhostTextWidgetModel {
public readonly sharedCache = this._register(new SharedInlineCompletionCache());
public readonly suggestWidgetAdapterModel = this._register(new SuggestWidgetPreviewModel(this.editor, this.sharedCache));
public readonly inlineCompletionsModel = this._register(new InlineCompletionsModel(this.editor, this.sharedCache, this.commandService));
public readonly inlineCompletionsModel = this._register(new InlineCompletionsModel(this.editor, this.sharedCache, this.commandService, this.languageConfigurationService));
public get activeInlineCompletionsModel(): InlineCompletionsModel | undefined {
if (this.targetModel === this.inlineCompletionsModel) {
@@ -79,7 +80,8 @@ export class GhostTextModel extends DelegatingModel implements GhostTextWidgetMo
constructor(
private readonly editor: IActiveCodeEditor,
@ICommandService private readonly commandService: ICommandService
@ICommandService private readonly commandService: ICommandService,
@ILanguageConfigurationService private readonly languageConfigurationService: ILanguageConfigurationService,
) {
super();
@@ -143,7 +145,7 @@ export class SharedInlineCompletionCache extends Disposable {
}
public setValue(editor: IActiveCodeEditor,
completionsSource: LiveInlineCompletions,
completionsSource: TrackedInlineCompletions,
triggerKind: InlineCompletionTriggerKind
) {
this.cache.value = new SynchronizedInlineCompletionsCache(
@@ -11,6 +11,9 @@ import { ITextModel } from 'vs/editor/common/model';
import { InlineCompletion } from 'vs/editor/common/languages';
import { GhostText, GhostTextPart } from 'vs/editor/contrib/inlineCompletions/browser/ghostText';
/**
* A normalized inline completion is an inline completion with a defined range.
*/
export interface NormalizedInlineCompletion extends InlineCompletion {
range: Range;
}
@@ -19,9 +19,11 @@ import { ITextModel } from 'vs/editor/common/model';
import { InlineCompletion, InlineCompletionContext, InlineCompletions, InlineCompletionsProvider, InlineCompletionsProviderRegistry, InlineCompletionTriggerKind } from 'vs/editor/common/languages';
import { BaseGhostTextWidgetModel, GhostText, GhostTextWidgetModel } from 'vs/editor/contrib/inlineCompletions/browser/ghostText';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { inlineSuggestCommitId } from './consts';
import { SharedInlineCompletionCache } from './ghostTextModel';
import { inlineCompletionToGhostText, NormalizedInlineCompletion } from './inlineCompletionToGhostText';
import { inlineSuggestCommitId } from 'vs/editor/contrib/inlineCompletions/browser/consts';
import { SharedInlineCompletionCache } from 'vs/editor/contrib/inlineCompletions/browser/ghostTextModel';
import { inlineCompletionToGhostText, NormalizedInlineCompletion } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionToGhostText';
import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry';
import { fixBracketsInLine } from 'vs/editor/common/model/bracketPairsTextModelPart/fixBrackets';
export class InlineCompletionsModel extends Disposable implements GhostTextWidgetModel {
protected readonly onDidChangeEmitter = new Emitter<void>();
@@ -36,6 +38,7 @@ export class InlineCompletionsModel extends Disposable implements GhostTextWidge
private readonly editor: IActiveCodeEditor,
private readonly cache: SharedInlineCompletionCache,
@ICommandService private readonly commandService: ICommandService,
@ILanguageConfigurationService private readonly languageConfigurationService: ILanguageConfigurationService,
) {
super();
@@ -138,7 +141,8 @@ export class InlineCompletionsModel extends Disposable implements GhostTextWidge
() => this.active,
this.commandService,
this.cache,
triggerKind
triggerKind,
this.languageConfigurationService
);
this.completionSession.value.takeOwnership(
this.completionSession.value.onDidChange(() => {
@@ -189,7 +193,8 @@ export class InlineCompletionsSession extends BaseGhostTextWidgetModel {
private readonly shouldUpdate: () => boolean,
private readonly commandService: ICommandService,
private readonly cache: SharedInlineCompletionCache,
private initialTriggerKind: InlineCompletionTriggerKind
private initialTriggerKind: InlineCompletionTriggerKind,
private readonly languageConfigurationService: ILanguageConfigurationService,
) {
super(editor);
@@ -310,7 +315,7 @@ export class InlineCompletionsSession extends BaseGhostTextWidgetModel {
return currentCompletion ? inlineCompletionToGhostText(currentCompletion, this.editor.getModel(), mode, this.editor.getPosition()) : undefined;
}
get currentCompletion(): LiveInlineCompletion | undefined {
get currentCompletion(): TrackedInlineCompletion | undefined {
const completion = this.currentCachedCompletion;
if (!completion) {
return undefined;
@@ -342,7 +347,8 @@ export class InlineCompletionsSession extends BaseGhostTextWidgetModel {
result = await provideInlineCompletions(position,
this.editor.getModel(),
{ triggerKind, selectedSuggestionInfo: undefined },
token
token,
this.languageConfigurationService
);
} catch (e) {
onUnexpectedError(e);
@@ -384,7 +390,7 @@ export class InlineCompletionsSession extends BaseGhostTextWidgetModel {
}
}
public commit(completion: LiveInlineCompletion): void {
public commit(completion: TrackedInlineCompletion): void {
// Mark the cache as stale, but don't dispose it yet,
// otherwise command args might get disposed.
const cache = this.cache.clearAndLeak();
@@ -428,7 +434,7 @@ export class SynchronizedInlineCompletionsCache extends Disposable {
constructor(
editor: IActiveCodeEditor,
completionsSource: LiveInlineCompletions,
completionsSource: TrackedInlineCompletions,
onChange: () => void,
public readonly triggerKind: InlineCompletionTriggerKind,
) {
@@ -486,13 +492,13 @@ class CachedInlineCompletion {
public synchronizedRange: Range;
constructor(
public readonly inlineCompletion: LiveInlineCompletion,
public readonly inlineCompletion: TrackedInlineCompletion,
public readonly decorationId: string,
) {
this.synchronizedRange = inlineCompletion.range;
}
public toLiveInlineCompletion(): LiveInlineCompletion | undefined {
public toLiveInlineCompletion(): TrackedInlineCompletion | undefined {
return {
text: this.inlineCompletion.text,
range: this.synchronizedRange,
@@ -504,16 +510,29 @@ class CachedInlineCompletion {
}
}
export interface LiveInlineCompletion extends NormalizedInlineCompletion {
/**
* A normalized inline completion that tracks which inline completion it has been constructed from.
*/
export interface TrackedInlineCompletion extends NormalizedInlineCompletion {
sourceProvider: InlineCompletionsProvider;
/**
* A reference to the original inline completion this inline completion has been constructed from.
* Used for event data to ensure referential equality.
*/
sourceInlineCompletion: InlineCompletion;
/**
* A reference to the original inline completion list this inline completion has been constructed from.
* Used for event data to ensure referential equality.
*/
sourceInlineCompletions: InlineCompletions;
}
/**
* Contains no duplicated items.
*/
export interface LiveInlineCompletions extends InlineCompletions<LiveInlineCompletion> {
export interface TrackedInlineCompletions extends InlineCompletions<TrackedInlineCompletion> {
dispose(): void;
}
@@ -531,8 +550,9 @@ export async function provideInlineCompletions(
position: Position,
model: ITextModel,
context: InlineCompletionContext,
token: CancellationToken = CancellationToken.None
): Promise<LiveInlineCompletions> {
token: CancellationToken = CancellationToken.None,
languageConfigurationService?: ILanguageConfigurationService
): Promise<TrackedInlineCompletions> {
const defaultReplaceRange = getDefaultRange(position, model);
const providers = InlineCompletionsProviderRegistry.all(model);
@@ -553,23 +573,38 @@ export async function provideInlineCompletions(
)
);
const itemsByHash = new Map<string, LiveInlineCompletion>();
const itemsByHash = new Map<string, TrackedInlineCompletion>();
for (const result of results) {
const completions = result.completions;
if (completions) {
for (const item of completions.items.map<LiveInlineCompletion>(item => ({
text: item.text,
range: item.range ? Range.lift(item.range) : defaultReplaceRange,
command: item.command,
sourceProvider: result.provider,
sourceInlineCompletions: completions,
sourceInlineCompletion: item
}))) {
if (item.range.startLineNumber !== item.range.endLineNumber) {
for (const item of completions.items) {
const range = item.range ? Range.lift(item.range) : defaultReplaceRange;
if (range.startLineNumber !== range.endLineNumber) {
// Ignore invalid ranges.
continue;
}
itemsByHash.set(JSON.stringify({ text: item.text, range: item.range }), item);
const text =
languageConfigurationService && item.completeBracketPairs
? closeBrackets(
item.text,
range.getStartPosition(),
model,
languageConfigurationService
)
: item.text;
const trackedItem: TrackedInlineCompletion = ({
text,
range,
command: item.command,
sourceProvider: result.provider,
sourceInlineCompletions: completions,
sourceInlineCompletion: item
});
itemsByHash.set(JSON.stringify({ text, range: item.range }), trackedItem);
}
}
}
@@ -584,6 +619,22 @@ export async function provideInlineCompletions(
};
}
function closeBrackets(text: string, position: Position, model: ITextModel, languageConfigurationService: ILanguageConfigurationService): string {
const lineStart = model.getLineContent(position.lineNumber).substring(0, position.column - 1);
const newLine = lineStart + text;
const newTokens = model.tokenizeLineWithEdit(position, newLine.length - (position.column - 1), text);
const slicedTokens = newTokens?.sliceAndInflate(position.column - 1, newLine.length, 0);
if (!slicedTokens) {
return text;
}
console.log(slicedTokens);
const newText = fixBracketsInLine(slicedTokens, languageConfigurationService);
return newText;
}
/**
* Shrinks the range if the text has a suffix/prefix that agrees with the text buffer.
* E.g. text buffer: `ab[cdef]ghi`, [...] is the replace range, `cxyzf` is the new text.
@@ -107,6 +107,13 @@ export class TestLineTokens implements IViewLineTokens {
throw new Error('Not implemented');
}
public getMetadata(tokenIndex: number): number {
throw new Error('Method not implemented.');
}
public getLanguageId(tokenIndex: number): string {
throw new Error('Method not implemented.');
}
}
export class TestLineTokenFactory {
+5
View File
@@ -6191,6 +6191,11 @@ declare namespace monaco.languages {
*/
readonly range?: IRange;
readonly command?: Command;
/**
* If set to `true`, unopened closing brackets are removed and unclosed opening brackets are closed.
* Defaults to `false`.
*/
readonly completeBracketPairs?: boolean;
}
export interface InlineCompletions<TItem extends InlineCompletion = InlineCompletion> {
@@ -1083,6 +1083,7 @@ class InlineCompletionAdapter {
range: item.range ? typeConvert.Range.from(item.range) : undefined,
command,
idx: idx,
completeBracketPairs: item.completeBracketPairs
});
}),
};
+5
View File
@@ -103,6 +103,11 @@ declare module 'vscode' {
*/
command?: Command;
/**
* If set to `true`, unopened closing brackets are removed and unclosed opening brackets are closed.
* Defaults to `false`.
*/
completeBracketPairs?: boolean;
constructor(text: string, range?: Range, command?: Command);
}