From 390a92c149abce07b79666f3737705ae7c3dfe8b Mon Sep 17 00:00:00 2001 From: Anthony Fu <anthonyfu117@hotmail.com> Date: Wed, 17 Jan 2024 17:42:56 +0100 Subject: [PATCH] fix!: rework highlight node (#13) --- packages/twoslash-vue/src/index.ts | 2 +- packages/twoslash/src/core.ts | 37 ++++--------------- packages/twoslash/src/legacy.ts | 7 ++-- packages/twoslash/src/types/nodes.ts | 4 +- packages/twoslash/src/types/returns.ts | 2 +- packages/twoslash/src/utils.ts | 1 + .../fixtures/throws/invalid-highlights.ts | 3 -- packages/twoslash/test/legacy.test.ts | 10 ++++- .../test/results/examples/highlighting.json | 8 ++-- .../test/results/tests/inline-highlights.json | 7 +--- .../results/throws/invalid-highlights.txt | 6 --- 11 files changed, 31 insertions(+), 56 deletions(-) delete mode 100644 packages/twoslash/test/fixtures/throws/invalid-highlights.ts delete mode 100644 packages/twoslash/test/results/throws/invalid-highlights.txt diff --git a/packages/twoslash-vue/src/index.ts b/packages/twoslash-vue/src/index.ts index db3b5a4..2ee126c 100644 --- a/packages/twoslash-vue/src/index.ts +++ b/packages/twoslash-vue/src/index.ts @@ -85,7 +85,7 @@ export function createTwoslasher(createOptions: CreateTwoslashVueOptions = {}): removals: [] as Range[], positionCompletions: [] as number[], positionQueries: [] as number[], - positionHighlights: [] as Range[], + positionHighlights: [] as TwoslashReturnMeta['positionHighlights'], flagNotations: [] as ParsedFlagNotation[], } satisfies Partial<TwoslashReturnMeta> diff --git a/packages/twoslash/src/core.ts b/packages/twoslash/src/core.ts index 5c488fa..711e66c 100644 --- a/packages/twoslash/src/core.ts +++ b/packages/twoslash/src/core.ts @@ -2,7 +2,7 @@ import type { CompilerOptions, CompletionEntry, CompletionTriggerKind, JsxEmit } import { createFSBackedSystem, createSystem, createVirtualTypeScriptEnvironment } from '@typescript/vfs' import { TwoslashError } from './error' import type { CompilerOptionDeclaration, CreateTwoslashOptions, NodeError, NodeWithoutPosition, Position, Range, TwoslashExecuteOptions, TwoslashInstance, TwoslashOptions, TwoslashReturn, TwoslashReturnMeta, VirtualFile } from './types' -import { areRangesIntersecting, createPositionConverter, findCutNotations, findFlagNotations, findQueryMarkers, getExtension, getIdentifierTextSpans, getObjectHash, isInRange, isInRanges, removeCodeRanges, removeTsExtension, resolveNodePositions, splitFiles, typesToExtension } from './utils' +import { createPositionConverter, findCutNotations, findFlagNotations, findQueryMarkers, getExtension, getIdentifierTextSpans, getObjectHash, isInRange, isInRanges, removeCodeRanges, removeTsExtension, resolveNodePositions, splitFiles, typesToExtension } from './utils' import { validateCodeForErrors } from './validation' import { defaultCompilerOptions, defaultHandbookOptions } from './defaults' @@ -239,35 +239,12 @@ export function createTwoslasher(createOptions: CreateTwoslashOptions = {}): Two // #region get highlights for (const highlight of meta.positionHighlights) { - if (isInRemoval(highlight[0])) { - throw new TwoslashError( - `Invalid highlight query`, - `The request on line ${pc.indexToPos(highlight[0]).line + 2} for highlight via ^^^ is in a removal range.`, - `This is likely that the positioning is off.`, - ) - } - - const file = getFileAtPosition(highlight[0])! - const identifiers = getIdentifiersOfFile(file) - - const ids = identifiers.filter(i => areRangesIntersecting(i as unknown as Range, highlight)) - const matched = ids - .map(i => getQuickInfo(file, i[0], i[2])) - .filter(Boolean) as NodeWithoutPosition[] - if (matched.length) { - for (const node of matched) { - node.type = 'highlight' - nodes.push(node) - } - } - else { - const pos = pc.indexToPos(highlight[0]) - throw new TwoslashError( - `Invalid highlight query`, - `The request on line ${pos.line + 2} in ${file.filename} for highlight via ^^^ is returned nothing from the compiler.`, - `This is likely that the positioning is off.`, - ) - } + nodes.push({ + type: 'highlight', + start: highlight[0], + length: highlight[1] - highlight[0], + text: highlight[2], + }) } // #endregion diff --git a/packages/twoslash/src/legacy.ts b/packages/twoslash/src/legacy.ts index 0a24244..8fd894e 100644 --- a/packages/twoslash/src/legacy.ts +++ b/packages/twoslash/src/legacy.ts @@ -136,11 +136,12 @@ export function convertLegacyReturn(result: TwoslashReturn): TwoslashReturnLegac highlights: result.highlights .map((h): TwoslashReturnLegacy['highlights'][0] => ({ kind: 'highlight', - offset: h.character, - start: h.start, + // it's a bit confusing that `offset` and `start` are flipped + offset: h.start, + start: h.character, length: h.length, line: h.line, - text: h.text, + text: h.text || '', })), queries: ([ diff --git a/packages/twoslash/src/types/nodes.ts b/packages/twoslash/src/types/nodes.ts index a2e37df..995bcf4 100644 --- a/packages/twoslash/src/types/nodes.ts +++ b/packages/twoslash/src/types/nodes.ts @@ -26,8 +26,10 @@ export interface NodeHover extends NodeBase { tags?: [name: string, text: string | undefined][] } -export interface NodeHighlight extends Omit<NodeHover, 'type'> { +export interface NodeHighlight extends NodeBase { type: 'highlight' + /** The annotation message */ + text?: string } export interface NodeQuery extends Omit<NodeHover, 'type'> { diff --git a/packages/twoslash/src/types/returns.ts b/packages/twoslash/src/types/returns.ts index 6d8664f..a126d5f 100644 --- a/packages/twoslash/src/types/returns.ts +++ b/packages/twoslash/src/types/returns.ts @@ -60,7 +60,7 @@ export interface TwoslashReturnMeta { /** * Positions of errors in the code */ - positionHighlights: Range[] + positionHighlights: [start: number, end: number, text?: string][] } export interface ParsedFlagNotation { diff --git a/packages/twoslash/src/utils.ts b/packages/twoslash/src/utils.ts index dd1fe15..a3d8840 100644 --- a/packages/twoslash/src/utils.ts +++ b/packages/twoslash/src/utils.ts @@ -443,6 +443,7 @@ export function findQueryMarkers( meta.positionHighlights.push([ targetIndex, targetIndex + markerLength, + match[2]?.trim(), ]) } }) diff --git a/packages/twoslash/test/fixtures/throws/invalid-highlights.ts b/packages/twoslash/test/fixtures/throws/invalid-highlights.ts deleted file mode 100644 index 72f5804..0000000 --- a/packages/twoslash/test/fixtures/throws/invalid-highlights.ts +++ /dev/null @@ -1,3 +0,0 @@ -const a = 1; -// ^^^ -console.log(a); diff --git a/packages/twoslash/test/legacy.test.ts b/packages/twoslash/test/legacy.test.ts index a507b34..30e5620 100644 --- a/packages/twoslash/test/legacy.test.ts +++ b/packages/twoslash/test/legacy.test.ts @@ -8,6 +8,8 @@ describe('legacy', async () => { await compareCode('./fixtures/examples/cuts-out-unnecessary-code.ts') await compareCode('./fixtures/examples/errors-with-generics.ts') await compareCode('./fixtures/examples/completions-basic.ts') + await compareCode('./fixtures/examples/highlighting.ts') + await compareCode('./fixtures/tests/inline-highlights.ts') async function compareCode(path: string) { const code = await fs.readFile(new URL(path, import.meta.url), 'utf-8') @@ -19,8 +21,8 @@ describe('legacy', async () => { function cleanup(t: any) { delete t.playgroundURL - // We have different calculations for queries, that are not trivial to map back t.queries.forEach((i: any) => { + // We have different calculations for queries, that are not trivial to map back delete i.start delete i.length delete i.text @@ -28,9 +30,15 @@ describe('legacy', async () => { }) t.errors.forEach((i: any) => { + // Id has a bit different calculation, as long as it's unique, it should be fine delete i.id }) + t.highlights.forEach((i: any) => { + // It seems the legacy version's line numbers are off for the second highlight nations + delete i.line + }) + return t } diff --git a/packages/twoslash/test/results/examples/highlighting.json b/packages/twoslash/test/results/examples/highlighting.json index 0cdb554..e2fa16e 100644 --- a/packages/twoslash/test/results/examples/highlighting.json +++ b/packages/twoslash/test/results/examples/highlighting.json @@ -96,12 +96,10 @@ }, { "type": "highlight", - "text": "var Date: DateConstructor\nnew () => Date (+4 overloads)", - "start": 138, - "length": 4, - "target": "Date", + "start": 134, + "length": 10, "line": 4, - "character": 22 + "character": 18 }, { "type": "hover", diff --git a/packages/twoslash/test/results/tests/inline-highlights.json b/packages/twoslash/test/results/tests/inline-highlights.json index 598c542..47aae16 100644 --- a/packages/twoslash/test/results/tests/inline-highlights.json +++ b/packages/twoslash/test/results/tests/inline-highlights.json @@ -3,10 +3,8 @@ "nodes": [ { "type": "highlight", - "text": "type Result = \"pass\" | \"fail\"", "start": 5, "length": 6, - "target": "Result", "line": 0, "character": 5 }, @@ -21,10 +19,9 @@ }, { "type": "highlight", - "text": "const hello: \"OK\"", "start": 37, - "length": 5, - "target": "hello", + "length": 2, + "text": "Sure", "line": 2, "character": 6 }, diff --git a/packages/twoslash/test/results/throws/invalid-highlights.txt b/packages/twoslash/test/results/throws/invalid-highlights.txt deleted file mode 100644 index fa63941..0000000 --- a/packages/twoslash/test/results/throws/invalid-highlights.txt +++ /dev/null @@ -1,6 +0,0 @@ - -## Invalid highlight query - -The request on line 2 in index.ts for highlight via ^^^ is returned nothing from the compiler. - -This is likely that the positioning is off. \ No newline at end of file