Path: blob/main/src/vs/workbench/contrib/preferences/common/smartSnippetInserter.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { JSONScanner, createScanner as createJSONScanner, SyntaxKind as JSONSyntaxKind } from '../../../../base/common/json.js';6import { Position } from '../../../../editor/common/core/position.js';7import { Range } from '../../../../editor/common/core/range.js';8import { ITextModel } from '../../../../editor/common/model.js';910export interface InsertSnippetResult {11position: Position;12prepend: string;13append: string;14}1516export class SmartSnippetInserter {1718private static hasOpenBrace(scanner: JSONScanner): boolean {1920while (scanner.scan() !== JSONSyntaxKind.EOF) {21const kind = scanner.getToken();2223if (kind === JSONSyntaxKind.OpenBraceToken) {24return true;25}26}2728return false;29}3031private static offsetToPosition(model: ITextModel, offset: number): Position {32let offsetBeforeLine = 0;33const eolLength = model.getEOL().length;34const lineCount = model.getLineCount();35for (let lineNumber = 1; lineNumber <= lineCount; lineNumber++) {36const lineTotalLength = model.getLineLength(lineNumber) + eolLength;37const offsetAfterLine = offsetBeforeLine + lineTotalLength;3839if (offsetAfterLine > offset) {40return new Position(41lineNumber,42offset - offsetBeforeLine + 143);44}45offsetBeforeLine = offsetAfterLine;46}47return new Position(48lineCount,49model.getLineMaxColumn(lineCount)50);51}5253static insertSnippet(model: ITextModel, _position: Position): InsertSnippetResult {5455const desiredPosition = model.getValueLengthInRange(new Range(1, 1, _position.lineNumber, _position.column));5657// <INVALID> [ <BEFORE_OBJECT> { <INVALID> } <AFTER_OBJECT>, <BEFORE_OBJECT> { <INVALID> } <AFTER_OBJECT> ] <INVALID>58enum State {59INVALID = 0,60AFTER_OBJECT = 1,61BEFORE_OBJECT = 2,62}63let currentState = State.INVALID;64let lastValidPos = -1;65let lastValidState = State.INVALID;6667const scanner = createJSONScanner(model.getValue());68let arrayLevel = 0;69let objLevel = 0;7071const checkRangeStatus = (pos: number, state: State) => {72if (state !== State.INVALID && arrayLevel === 1 && objLevel === 0) {73currentState = state;74lastValidPos = pos;75lastValidState = state;76} else {77if (currentState !== State.INVALID) {78currentState = State.INVALID;79lastValidPos = scanner.getTokenOffset();80}81}82};8384while (scanner.scan() !== JSONSyntaxKind.EOF) {85const currentPos = scanner.getPosition();86const kind = scanner.getToken();8788let goodKind = false;89switch (kind) {90case JSONSyntaxKind.OpenBracketToken:91goodKind = true;92arrayLevel++;93checkRangeStatus(currentPos, State.BEFORE_OBJECT);94break;95case JSONSyntaxKind.CloseBracketToken:96goodKind = true;97arrayLevel--;98checkRangeStatus(currentPos, State.INVALID);99break;100case JSONSyntaxKind.CommaToken:101goodKind = true;102checkRangeStatus(currentPos, State.BEFORE_OBJECT);103break;104case JSONSyntaxKind.OpenBraceToken:105goodKind = true;106objLevel++;107checkRangeStatus(currentPos, State.INVALID);108break;109case JSONSyntaxKind.CloseBraceToken:110goodKind = true;111objLevel--;112checkRangeStatus(currentPos, State.AFTER_OBJECT);113break;114case JSONSyntaxKind.Trivia:115case JSONSyntaxKind.LineBreakTrivia:116goodKind = true;117}118119if (currentPos >= desiredPosition && (currentState !== State.INVALID || lastValidPos !== -1)) {120let acceptPosition: number;121let acceptState: State;122123if (currentState !== State.INVALID) {124acceptPosition = (goodKind ? currentPos : scanner.getTokenOffset());125acceptState = currentState;126} else {127acceptPosition = lastValidPos;128acceptState = lastValidState;129}130131if (acceptState as State === State.AFTER_OBJECT) {132return {133position: this.offsetToPosition(model, acceptPosition),134prepend: ',',135append: ''136};137} else {138scanner.setPosition(acceptPosition);139return {140position: this.offsetToPosition(model, acceptPosition),141prepend: '',142append: this.hasOpenBrace(scanner) ? ',' : ''143};144}145}146}147148// no valid position found!149const modelLineCount = model.getLineCount();150return {151position: new Position(modelLineCount, model.getLineMaxColumn(modelLineCount)),152prepend: '\n[',153append: ']'154};155}156}157158159