Path: blob/main/extensions/emmet/src/incrementDecrement.ts
4772 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*--------------------------------------------------------------------------------------------*/45/* Based on @sergeche's work in his emmet plugin */67import * as vscode from 'vscode';89const reNumber = /[0-9]/;1011/**12* Incerement number under caret of given editor13*/14export function incrementDecrement(delta: number): Thenable<boolean> | undefined {15if (!vscode.window.activeTextEditor) {16vscode.window.showInformationMessage('No editor is active');17return;18}19const editor = vscode.window.activeTextEditor;2021return editor.edit(editBuilder => {22editor.selections.forEach(selection => {23const rangeToReplace = locate(editor.document, selection.isReversed ? selection.anchor : selection.active);24if (!rangeToReplace) {25return;26}2728const text = editor.document.getText(rangeToReplace);29if (isValidNumber(text)) {30editBuilder.replace(rangeToReplace, update(text, delta));31}32});33});34}3536/**37* Updates given number with `delta` and returns string formatted according38* to original string format39*/40export function update(numString: string, delta: number): string {41let m: RegExpMatchArray | null;42const decimals = (m = numString.match(/\.(\d+)$/)) ? m[1].length : 1;43let output = String((parseFloat(numString) + delta).toFixed(decimals)).replace(/\.0+$/, '');4445if (m = numString.match(/^\-?(0\d+)/)) {46// padded number: preserve padding47output = output.replace(/^(\-?)(\d+)/, (_, minus, prefix) =>48minus + '0'.repeat(Math.max(0, (m ? m[1].length : 0) - prefix.length)) + prefix);49}5051if (/^\-?\./.test(numString)) {52// omit integer part53output = output.replace(/^(\-?)0+/, '$1');54}5556return output;57}5859/**60* Locates number from given position in the document61*62* @return Range of number or `undefined` if not found63*/64export function locate(document: vscode.TextDocument, pos: vscode.Position): vscode.Range | undefined {6566const line = document.lineAt(pos.line).text;67let start = pos.character;68let end = pos.character;69let hadDot = false, hadMinus = false;70let ch;7172while (start > 0) {73ch = line[--start];74if (ch === '-') {75hadMinus = true;76break;77} else if (ch === '.' && !hadDot) {78hadDot = true;79} else if (!reNumber.test(ch)) {80start++;81break;82}83}8485if (line[end] === '-' && !hadMinus) {86end++;87}8889while (end < line.length) {90ch = line[end++];91if (ch === '.' && !hadDot && reNumber.test(line[end])) {92// A dot must be followed by a number. Otherwise stop parsing93hadDot = true;94} else if (!reNumber.test(ch)) {95end--;96break;97}98}99100// ensure that found range contains valid number101if (start !== end && isValidNumber(line.slice(start, end))) {102return new vscode.Range(pos.line, start, pos.line, end);103}104105return;106}107108/**109* Check if given string contains valid number110*/111function isValidNumber(str: string): boolean {112return str ? !isNaN(parseFloat(str)) : false;113}114115116