From e6d1eb9603ca36b3c3fbf81249b4f168d92e0c62 Mon Sep 17 00:00:00 2001 From: Christian Holbrook Date: Thu, 4 Sep 2025 13:31:40 -0600 Subject: [PATCH 1/4] Remove unused utils, also marked some that could maybe just move to a unit test utils file --- src/util.spec.ts | 6 -- src/util.ts | 200 ++--------------------------------------------- 2 files changed, 5 insertions(+), 201 deletions(-) diff --git a/src/util.spec.ts b/src/util.spec.ts index 2e02e865c..f263895b3 100644 --- a/src/util.spec.ts +++ b/src/util.spec.ts @@ -503,12 +503,6 @@ describe('util', () => { }); }); - describe('padLeft', () => { - it('stops at an upper limit to prevent terrible memory explosions', () => { - expect(util.padLeft('', Number.MAX_VALUE, ' ')).to.be.lengthOf(1000); - }); - }); - describe('getTextForRange', () => { const testArray = ['The quick', 'brown fox', 'jumps over', 'the lazy dog']; const testString = testArray.join('\n'); diff --git a/src/util.ts b/src/util.ts index db783aa19..333b2f9e2 100644 --- a/src/util.ts +++ b/src/util.ts @@ -136,24 +136,10 @@ export class Util { return 'pkg:/' + pkgPath.replace(/^pkg:\//i, ''); } - /** - * Determine if the given path starts with a protocol - */ - public startsWithProtocol(path: string) { - return !!/^[-a-z]+:\//i.exec(path); - } - - /** - * Given a pkg path of any kind, transform it to a roku-specific pkg path (i.e. "pkg:/some/path.brs") - * @deprecated use `sanitizePkgPath instead. Will be removed in v1 - */ - public getRokuPkgPath(pkgPath: string) { - return this.sanitizePkgPath(pkgPath); - } - /** * Given a path to a file/directory, replace all path separators with the current system's version. */ + //TODO check this? maybe it can move to testHelpers public pathSepNormalize(filePath: string, separator?: string) { if (!filePath) { return filePath; @@ -307,38 +293,6 @@ export class Util { collection[key] = [...result]; } - /** - * Do work within the scope of a changed current working directory - * @param targetCwd the cwd where the work should be performed - * @param callback a function to call when the cwd has been changed to `targetCwd` - */ - public cwdWork(targetCwd: string | null | undefined, callback: () => T): T { - let originalCwd = process.cwd(); - if (targetCwd) { - process.chdir(targetCwd); - } - - let result: T; - let err; - - try { - result = callback(); - } catch (e) { - err = e; - } - - if (targetCwd) { - process.chdir(originalCwd); - } - - if (err) { - throw err; - } else { - //justification: `result` is set as long as `err` is not set and vice versa - return result!; - } - } - /** * Given a BsConfig object, start with defaults, * merge with bsconfig.json and the provided options. @@ -467,13 +421,6 @@ export class Util { return result; } - /** - * Split a file by newline characters (LF or CRLF) - */ - public getLines(text: string) { - return text.split(/\r?\n/); - } - /** * Given an absolute path to a source file, and a target path, * compute the pkg path for the target relative to the source file's location @@ -774,39 +721,6 @@ export class Util { return 'roSGNode' + sgNodeName; } - /** - * Parse an xml file and get back a javascript object containing its results - */ - public parseXml(text: string) { - return new Promise((resolve, reject) => { - xml2js.parseString(text, (err, data) => { - if (err) { - reject(err); - } else { - resolve(data); - } - }); - }); - } - - public propertyCount(object: Record) { - let count = 0; - for (let key in object) { - if (object.hasOwnProperty(key)) { - count++; - } - } - return count; - } - - public padLeft(subject: string, totalLength: number, char: string) { - totalLength = totalLength > 1000 ? 1000 : totalLength; - while (subject.length < totalLength) { - subject = char + subject; - } - return subject; - } - /** * Force the drive letter to lower case */ @@ -842,6 +756,7 @@ export class Util { * Determine if two arrays containing primitive values are equal. * This considers order and compares by equality. */ + //TODO maybe this can move to testHelpers public areArraysEqual(arr1: any[], arr2: any[]) { if (arr1.length !== arr2.length) { return false; @@ -937,6 +852,7 @@ export class Util { /** * Walks up the chain to find the closest bsconfig.json file */ + //TODO maybe this can move to testHelpers public async findClosestConfigFile(currentPath: string): Promise { //make the path absolute currentPath = path.resolve( @@ -979,15 +895,6 @@ export class Util { }); } - /** - * Given an array, map and then flatten - * @param array the array to flatMap over - * @param callback a function that is called for every array item - */ - public flatMap(array: T[], callback: (arg: T) => R[]): R[] { - return Array.prototype.concat.apply([], array.map(callback)); - } - /** * Determines if the position is greater than the range. This means * the position does not touch the range, and has a position greater than the end @@ -1029,30 +936,6 @@ export class Util { } } - - /** - * Get a location object back by extracting location information from other objects that contain location - */ - public getRange(startObj: | { range: Range }, endObj: { range: Range }): Range { - if (!startObj?.range || !endObj?.range) { - return undefined; - } - return util.createRangeFromPositions(startObj.range?.start, endObj.range?.end); - } - - /** - * If the two items both start on the same line - */ - public sameStartLine(first: { range: Range }, second: { range: Range }) { - if (first && second && (first.range !== undefined) && (second.range !== undefined) && - first.range.start.line === second.range.start.line - ) { - return true; - } else { - return false; - } - } - /** * If the two items have lines that touch */ @@ -1071,19 +954,6 @@ export class Util { } } - /** - * Given text with (or without) dots separating text, get the rightmost word. - * (i.e. given "A.B.C", returns "C". or "B" returns "B because there's no dot) - */ - public getTextAfterFinalDot(name: string) { - if (name) { - let parts = name.split('.'); - if (parts.length > 0) { - return parts[parts.length - 1]; - } - } - } - /** * Find a script import that the current position touches, or undefined if not found */ @@ -1364,6 +1234,7 @@ export class Util { /** * Convert a list of tokens into a string, including their leading whitespace */ + //TODO maybe this can move to testHelpers public tokensToString(tokens: Token[]) { let result = ''; //skip iterating the final token @@ -2101,19 +1972,6 @@ export class Util { return result; } - /** - * Get the first locatable item found at the specified position - * @param locatables an array of items that have a `range` property - * @param position the position that the locatable must contain - */ - public getFirstLocatableAt(locatables: Locatable[], position: Position) { - for (let token of locatables) { - if (util.rangeContains(token.location?.range, position)) { - return token; - } - } - } - /** * Sort an array of objects that have a Range */ @@ -2163,6 +2021,7 @@ export class Util { * Split the given text and return ranges for each chunk. * Only works for single-line strings */ + //TODO maybe move to testHelpers public splitGetRange(separator: string, text: string, range: Range) { const chunks = text.split(separator); const result = [] as Array<{ text: string; range: Range }>; @@ -2259,16 +2118,6 @@ export class Util { /* eslint-enable no-var */ } - public stringJoin(strings: string[], separator: string) { - // eslint-disable-next-line no-var - var result = strings[0] ?? ''; - // eslint-disable-next-line no-var - for (var i = 1; i < strings.length; i++) { - result += separator + strings[i]; - } - return result; - } - /** * Break an expression into each part. */ @@ -2292,45 +2141,6 @@ export class Util { return parts; } - /** - * Break an expression into each part, and return any VariableExpression or DottedGet expresisons from left-to-right. - */ - public getDottedGetPath(expression: Expression): [VariableExpression, ...DottedGetExpression[]] { - let parts: Expression[] = []; - let nextPart = expression; - loop: while (nextPart) { - switch (nextPart?.kind) { - case AstNodeKind.DottedGetExpression: - parts.push(nextPart); - nextPart = (nextPart as DottedGetExpression).obj; - continue; - case AstNodeKind.IndexedGetExpression: - case AstNodeKind.XmlAttributeGetExpression: - nextPart = (nextPart as IndexedGetExpression | XmlAttributeGetExpression).obj; - parts = []; - continue; - case AstNodeKind.CallExpression: - case AstNodeKind.CallfuncExpression: - nextPart = (nextPart as CallExpression | CallfuncExpression).callee; - parts = []; - continue; - case AstNodeKind.NewExpression: - nextPart = (nextPart as NewExpression).call.callee; - parts = []; - continue; - case AstNodeKind.TypeExpression: - nextPart = (nextPart as TypeExpression).expression; - continue; - case AstNodeKind.VariableExpression: - parts.push(nextPart); - break loop; - default: - return [] as any; - } - } - return parts.reverse() as any; - } - /** * Returns an integer if valid, or undefined. Eliminates checking for NaN */ From 013ef8d328ae7c2a8e37407a8bdc59328cd36c7d Mon Sep 17 00:00:00 2001 From: Christian Holbrook Date: Thu, 4 Sep 2025 13:49:58 -0600 Subject: [PATCH 2/4] Fix compiler errors --- src/util.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/util.ts b/src/util.ts index 333b2f9e2..c00b0d755 100644 --- a/src/util.ts +++ b/src/util.ts @@ -7,7 +7,6 @@ import { rokuDeploy, DefaultFiles } from 'roku-deploy'; import type { Diagnostic, Position, DiagnosticRelatedInformation } from 'vscode-languageserver'; import { Range, Location } from 'vscode-languageserver'; import { URI } from 'vscode-uri'; -import * as xml2js from 'xml2js'; import type { BsConfig, FinalizedBsConfig } from './BsConfig'; import { DiagnosticMessages } from './DiagnosticMessages'; import type { CallableContainer, BsDiagnostic, FileReference, CallableContainerMap, Plugin, ExpressionInfo, TranspileResult, MaybePromise, DisposableLike, ExtraSymbolData, GetTypeOptions, TypeChainProcessResult, PluginFactory } from './interfaces'; @@ -22,9 +21,9 @@ import { ObjectType } from './types/ObjectType'; import { StringType } from './types/StringType'; import { VoidType } from './types/VoidType'; import { ParseMode } from './parser/Parser'; -import type { CallExpression, CallfuncExpression, DottedGetExpression, FunctionParameterExpression, IndexedGetExpression, LiteralExpression, NewExpression, TypeExpression, VariableExpression, XmlAttributeGetExpression } from './parser/Expression'; +import type { CallExpression, CallfuncExpression, DottedGetExpression, FunctionParameterExpression, IndexedGetExpression, LiteralExpression, TypeExpression, VariableExpression } from './parser/Expression'; import { LogLevel, createLogger } from './logging'; -import { isToken, type Identifier, type Locatable, type Token } from './lexer/Token'; +import { isToken, type Identifier, type Token } from './lexer/Token'; import { TokenKind } from './lexer/TokenKind'; import { isAnyReferenceType, isBinaryExpression, isBooleanTypeLike, isBrsFile, isCallExpression, isCallableType, isCallfuncExpression, isClassType, isComponentType, isDottedGetExpression, isDoubleTypeLike, isDynamicType, isEnumMemberType, isExpression, isFloatTypeLike, isIndexedGetExpression, isIntegerTypeLike, isInvalidTypeLike, isLiteralString, isLongIntegerTypeLike, isNamespaceStatement, isNamespaceType, isNewExpression, isNumberType, isObjectType, isPrimitiveType, isReferenceType, isStatement, isStringTypeLike, isTypeExpression, isTypedArrayExpression, isTypedFunctionType, isUninitializedType, isUnionType, isVariableExpression, isVoidType, isXmlAttributeGetExpression, isXmlFile } from './astUtils/reflection'; import { WalkMode } from './astUtils/visitors'; From 1bbd1030d168256035e5dde38797f23cb3b9df50 Mon Sep 17 00:00:00 2001 From: Christian Holbrook Date: Mon, 8 Sep 2025 15:27:48 -0600 Subject: [PATCH 3/4] Move out functions that only support tests --- src/lexer/Lexer.spec.ts | 13 ++++- src/util.spec.ts | 126 +++++++++++++++++++++++++++++++++------- src/util.ts | 100 ------------------------------- 3 files changed, 117 insertions(+), 122 deletions(-) diff --git a/src/lexer/Lexer.spec.ts b/src/lexer/Lexer.spec.ts index 2f245373f..de8895f15 100644 --- a/src/lexer/Lexer.spec.ts +++ b/src/lexer/Lexer.spec.ts @@ -1338,6 +1338,17 @@ describe('lexer', () => { }); it('properly tracks leadingWhitespace', () => { + /** + * Convert a list of tokens into a string, including their leading whitespace + */ + function tokensToString(tokens: Token[]) { + let result = ''; + //skip iterating the final token + for (let token of tokens) { + result += token.leadingWhitespace + token.text; + } + return result; + } const text = ` sub main() @@ -1346,7 +1357,7 @@ describe('lexer', () => { end sub `; const { tokens } = Lexer.scan(text, { includeWhitespace: false }); - expect(util.tokensToString(tokens)).to.equal(text); + expect(tokensToString(tokens)).to.equal(text); }); it('properly detects try/catch tokens', () => { diff --git a/src/util.spec.ts b/src/util.spec.ts index 2c802d294..b507bc5ff 100644 --- a/src/util.spec.ts +++ b/src/util.spec.ts @@ -140,12 +140,12 @@ describe('util', () => { ] }; util.resolvePathsRelativeTo(config, 'plugins', s`${rootDir}/config`); - expect(config?.plugins?.map(p => (p ? util.pathSepNormalize(p, '/') : undefined))).to.deep.equal([ + expect(config?.plugins?.map(p => (p ? pathSepNormalize(p, '/') : undefined))).to.deep.equal([ `${rootDir}/config/plugins.js`, `${rootDir}/config/scripts/plugins.js`, `${rootDir}/scripts/plugins.js`, 'bsplugin' - ].map(p => util.pathSepNormalize(p, '/'))); + ].map(p => pathSepNormalize(p, '/'))); }); it('resolves path relatively to config file', () => { @@ -175,10 +175,10 @@ describe('util', () => { ] }; util.resolvePathsRelativeTo(config, 'plugins', s`${process.cwd()}/config`); - expect(config?.plugins?.map(p => (p ? util.pathSepNormalize(p, '/') : undefined))).to.deep.equal([ + expect(config?.plugins?.map(p => (p ? pathSepNormalize(p, '/') : undefined))).to.deep.equal([ s`${process.cwd()}/config/plugins.js`, 'bsplugin' - ].map(p => util.pathSepNormalize(p, '/'))); + ].map(p => pathSepNormalize(p, '/'))); }); }); @@ -232,8 +232,8 @@ describe('util', () => { describe('pathSepNormalize', () => { it('works for both types of separators', () => { - expect(util.pathSepNormalize('c:/some\\path', '\\')).to.equal('c:\\some\\path'); - expect(util.pathSepNormalize('c:/some\\path', '/')).to.equal('c:/some/path'); + expect(pathSepNormalize('c:/some\\path', '\\')).to.equal('c:\\some\\path'); + expect(pathSepNormalize('c:/some\\path', '/')).to.equal('c:/some/path'); }); it('does not throw when given `undefined`', () => { expect(undefined).to.be.undefined; @@ -250,6 +250,37 @@ describe('util', () => { }); describe('findClosestConfigFile', () => { + + /** + * Walks up the chain to find the closest bsconfig.json file + */ + async function findClosestConfigFile(currentPath: string): Promise { + //make the path absolute + currentPath = path.resolve( + path.normalize( + currentPath + ) + ); + + let previousPath: string | undefined; + //using ../ on the root of the drive results in the same file path, so that's how we know we reached the top + while (previousPath !== currentPath) { + previousPath = currentPath; + + let bsPath = path.join(currentPath, 'bsconfig.json'); + let brsPath = path.join(currentPath, 'brsconfig.json'); + if (await util.pathExists(bsPath)) { + return bsPath; + } else if (await util.pathExists(brsPath)) { + return brsPath; + } else { + //walk upwards one directory + currentPath = path.resolve(path.join(currentPath, '../')); + } + } + //got to the root path, no config file exists + } + it('finds config up the chain', async () => { const brsFilePath = s`${rootDir}/src/app.brs`; const currentDirBsConfigPath = s`${rootDir}/src/bsconfig.json`; @@ -262,13 +293,13 @@ describe('util', () => { fsExtra.outputFileSync(parentDirBsConfigPath, ''); fsExtra.outputFileSync(parentDirBrsConfigPath, ''); - expect(await util.findClosestConfigFile(brsFilePath)).to.equal(currentDirBsConfigPath); + expect(await findClosestConfigFile(brsFilePath)).to.equal(currentDirBsConfigPath); fsExtra.removeSync(currentDirBsConfigPath); - expect(await util.findClosestConfigFile(brsFilePath)).to.equal(currentDirBrsConfigPath); + expect(await findClosestConfigFile(brsFilePath)).to.equal(currentDirBrsConfigPath); fsExtra.removeSync(currentDirBrsConfigPath); - expect(await util.findClosestConfigFile(brsFilePath)).to.equal(parentDirBsConfigPath); + expect(await findClosestConfigFile(brsFilePath)).to.equal(parentDirBsConfigPath); fsExtra.removeSync(parentDirBsConfigPath); - expect(await util.findClosestConfigFile(brsFilePath)).to.equal(parentDirBrsConfigPath); + expect(await findClosestConfigFile(brsFilePath)).to.equal(parentDirBrsConfigPath); }); }); @@ -484,17 +515,33 @@ describe('util', () => { }); describe('areArraysEqual', () => { + /** + * Determine if two arrays containing primitive values are equal. + * This considers order and compares by equality. + */ + function areArraysEqual(arr1: any[], arr2: any[]) { + if (arr1.length !== arr2.length) { + return false; + } + for (let i = 0; i < arr1.length; i++) { + if (arr1[i] !== arr2[i]) { + return false; + } + } + return true; + } + it('finds equal arrays', () => { - expect(util.areArraysEqual([1, 2], [1, 2])).to.be.true; - expect(util.areArraysEqual(['cat', 'dog'], ['cat', 'dog'])).to.be.true; + expect(areArraysEqual([1, 2], [1, 2])).to.be.true; + expect(areArraysEqual(['cat', 'dog'], ['cat', 'dog'])).to.be.true; }); it('detects non-equal arrays', () => { - expect(util.areArraysEqual([1, 2], [1])).to.be.false; - expect(util.areArraysEqual([1, 2], [2])).to.be.false; - expect(util.areArraysEqual([2], [1])).to.be.false; - expect(util.areArraysEqual([2], [0])).to.be.false; - expect(util.areArraysEqual(['cat', 'dog'], ['cat', 'dog', 'mouse'])).to.be.false; - expect(util.areArraysEqual(['cat', 'dog'], ['dog', 'cat'])).to.be.false; + expect(areArraysEqual([1, 2], [1])).to.be.false; + expect(areArraysEqual([1, 2], [2])).to.be.false; + expect(areArraysEqual([2], [1])).to.be.false; + expect(areArraysEqual([2], [0])).to.be.false; + expect(areArraysEqual(['cat', 'dog'], ['cat', 'dog', 'mouse'])).to.be.false; + expect(areArraysEqual(['cat', 'dog'], ['dog', 'cat'])).to.be.false; }); }); @@ -949,9 +996,34 @@ describe('util', () => { }); describe('splitWithLocation', () => { + /** + * Split the given text and return ranges for each chunk. + * Only works for single-line strings + */ + function splitGetRange(separator: string, text: string, range: Range) { + const chunks = text.split(separator); + const result = [] as Array<{ text: string; range: Range }>; + let offset = 0; + for (let chunk of chunks) { + //only keep nonzero chunks + if (chunk.length > 0) { + result.push({ + text: chunk, + range: util.createRange( + range.start.line, + range.start.character + offset, + range.end.line, + range.start.character + offset + chunk.length + ) + }); + } + offset += chunk.length + separator.length; + } + return result; + } it('works with no split items', () => { expect( - util.splitGetRange('.', 'hello', util.createRange(2, 10, 2, 15)) + splitGetRange('.', 'hello', util.createRange(2, 10, 2, 15)) ).to.eql([{ text: 'hello', range: util.createRange(2, 10, 2, 15) @@ -960,7 +1032,7 @@ describe('util', () => { it('handles empty chunks', () => { expect( - util.splitGetRange('l', 'hello', util.createRange(2, 10, 2, 15)) + splitGetRange('l', 'hello', util.createRange(2, 10, 2, 15)) ).to.eql([{ text: 'he', range: util.createRange(2, 10, 2, 12) @@ -972,7 +1044,7 @@ describe('util', () => { it('handles multiple non-empty chunks', () => { expect( - util.splitGetRange('.', 'abc.d.efgh.i', util.createRange(2, 10, 2, 2)) + splitGetRange('.', 'abc.d.efgh.i', util.createRange(2, 10, 2, 2)) ).to.eql([{ text: 'abc', range: util.createRange(2, 10, 2, 13) @@ -1692,3 +1764,15 @@ describe('util', () => { }); }); }); + + +/** + * Given a path to a file/directory, replace all path separators with the current system's version. + */ +function pathSepNormalize(filePath: string, separator?: string) { + if (!filePath) { + return filePath; + } + separator = separator ? separator : path.sep; + return filePath.replace(/[\\/]+/g, separator); +} diff --git a/src/util.ts b/src/util.ts index cff918675..84fcf79c1 100644 --- a/src/util.ts +++ b/src/util.ts @@ -135,18 +135,6 @@ export class Util { return 'pkg:/' + pkgPath.replace(/^pkg:\//i, ''); } - /** - * Given a path to a file/directory, replace all path separators with the current system's version. - */ - //TODO check this? maybe it can move to testHelpers - public pathSepNormalize(filePath: string, separator?: string) { - if (!filePath) { - return filePath; - } - separator = separator ? separator : path.sep; - return filePath.replace(/[\\/]+/g, separator); - } - /** * Find the path to the config file. * If the config file path doesn't exist @@ -762,22 +750,6 @@ export class Util { } } - /** - * Determine if two arrays containing primitive values are equal. - * This considers order and compares by equality. - */ - //TODO maybe this can move to testHelpers - public areArraysEqual(arr1: any[], arr2: any[]) { - if (arr1.length !== arr2.length) { - return false; - } - for (let i = 0; i < arr1.length; i++) { - if (arr1[i] !== arr2[i]) { - return false; - } - } - return true; - } /** * Does the string appear to be a uri (i.e. does it start with `file:`) */ @@ -844,38 +816,6 @@ export class Util { } } - - /** - * Walks up the chain to find the closest bsconfig.json file - */ - //TODO maybe this can move to testHelpers - public async findClosestConfigFile(currentPath: string): Promise { - //make the path absolute - currentPath = path.resolve( - path.normalize( - currentPath - ) - ); - - let previousPath: string | undefined; - //using ../ on the root of the drive results in the same file path, so that's how we know we reached the top - while (previousPath !== currentPath) { - previousPath = currentPath; - - let bsPath = path.join(currentPath, 'bsconfig.json'); - let brsPath = path.join(currentPath, 'brsconfig.json'); - if (await this.pathExists(bsPath)) { - return bsPath; - } else if (await this.pathExists(brsPath)) { - return brsPath; - } else { - //walk upwards one directory - currentPath = path.resolve(path.join(currentPath, '../')); - } - } - //got to the root path, no config file exists - } - /** * Set a timeout for the specified milliseconds, and resolve the promise once the timeout is finished. * @param milliseconds the minimum number of milliseconds to sleep for @@ -1227,19 +1167,6 @@ export class Util { }; } - /** - * Convert a list of tokens into a string, including their leading whitespace - */ - //TODO maybe this can move to testHelpers - public tokensToString(tokens: Token[]) { - let result = ''; - //skip iterating the final token - for (let token of tokens) { - result += token.leadingWhitespace + token.text; - } - return result; - } - /** * Convert a token into a BscType */ @@ -2013,33 +1940,6 @@ export class Util { }); } - /** - * Split the given text and return ranges for each chunk. - * Only works for single-line strings - */ - //TODO maybe move to testHelpers - public splitGetRange(separator: string, text: string, range: Range) { - const chunks = text.split(separator); - const result = [] as Array<{ text: string; range: Range }>; - let offset = 0; - for (let chunk of chunks) { - //only keep nonzero chunks - if (chunk.length > 0) { - result.push({ - text: chunk, - range: this.createRange( - range.start.line, - range.start.character + offset, - range.end.line, - range.start.character + offset + chunk.length - ) - }); - } - offset += chunk.length + separator.length; - } - return result; - } - /** * Wrap the given code in a markdown code fence (with the language) */ From 46bf5c13a73c2294a0bacfae4633c7863c46f6a3 Mon Sep 17 00:00:00 2001 From: Christian Holbrook Date: Tue, 9 Sep 2025 08:06:55 -0600 Subject: [PATCH 4/4] Remove unused package --- src/lexer/Lexer.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lexer/Lexer.spec.ts b/src/lexer/Lexer.spec.ts index de8895f15..f84cb1e7c 100644 --- a/src/lexer/Lexer.spec.ts +++ b/src/lexer/Lexer.spec.ts @@ -7,7 +7,6 @@ import type { Token } from './Token'; import { isToken } from './Token'; import { rangeToArray } from '../parser/Parser.spec'; import { Range } from 'vscode-languageserver'; -import util from '../util'; import { DiagnosticMessages } from '../DiagnosticMessages'; describe('lexer', () => {