diff --git a/src/lib/utils/jsonUtils.test.ts b/src/lib/utils/jsonUtils.test.ts index 6b8753c1..6c1c295f 100644 --- a/src/lib/utils/jsonUtils.test.ts +++ b/src/lib/utils/jsonUtils.test.ts @@ -17,8 +17,11 @@ import { toJSONContent, toTextContent, validateContentType, - parseAndRepairOrUndefined + parseAndRepairOrUndefined, + findTextLocation, + findSelectionFromTextLocation } from './jsonUtils.js' +import { createMultiSelection } from 'svelte-jsoneditor' const LosslessJSONParser = { parse, stringify } @@ -439,4 +442,34 @@ describe('jsonUtils', () => { // expect(parseAndRepairOrUndefined('0123', JSON)).toEqual('0123') // FIXME }) }) + + describe('findTextLocation', () => { + // TODO: write more unit tests for findTextLocation + test('find a text location', () => { + const text = `{\n "object": {\n "a":2 \n}\n}` + const path = ['object', 'a'] + expect(findTextLocation(text, path)).toEqual({ + path, + line: 2, + column: 4, + from: 20, + to: 23 + }) + }) + }) + + describe('findSelectionFromTextLocation', () => { + // TODO: write more unit tests for findSelectionFromTextLocation + test('find the path', () => { + const text = `{\n "object": {\n "a":2 \n}\n}` + + expect(findSelectionFromTextLocation(text, 20, 20)).toEqual( + createMultiSelection(['object', 'a'], ['object', 'a']) + ) + + expect(findSelectionFromTextLocation(text, 23, 24)).toEqual( + createMultiSelection(['object', 'a'], ['object', 'a']) + ) + }) + }) }) diff --git a/src/lib/utils/jsonUtils.ts b/src/lib/utils/jsonUtils.ts index e3fb39d5..d9121426 100644 --- a/src/lib/utils/jsonUtils.ts +++ b/src/lib/utils/jsonUtils.ts @@ -1,4 +1,4 @@ -import type { JSONPath } from 'immutable-json-patch' +import { type JSONPath, parseJSONPointer } from 'immutable-json-patch' import { compileJSONPointer } from 'immutable-json-patch' // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore @@ -10,11 +10,13 @@ import type { Content, JSONContent, JSONParser, + JSONSelection, ParseError, TextContent, TextLocation } from '../types' import { int } from './numberUtils.js' +import { createMultiSelection } from '$lib/logic/selection' /** * Parse the JSON. if this fails, try to repair and parse. @@ -201,7 +203,6 @@ export function countCharacterOccurrences( /** * Find the text location of a JSON path */ -// TODO: write unit tests export function findTextLocation(text: string, path: JSONPath): TextLocation { try { const jsmap = jsonSourceMap.parse(text) @@ -230,6 +231,43 @@ export function findTextLocation(text: string, path: JSONPath): TextLocation { } } +/** + * Find the text location from a JSON path + * Returns null when not found (either not exists, or the document cannot be parsed) + */ +export function findSelectionFromTextLocation( + text: string, + anchor: number, + head: number +): JSONSelection | null { + try { + const jsmap = jsonSourceMap.parse(text) + const pointers = Object.keys(jsmap.pointers) + + let anchorPath: JSONPath = [] + let focusPath: JSONPath = [] + + for (const pointer of pointers) { + const state = jsmap.pointers[pointer] + + // TODO: work out creating a KeySelection, ValueSelection, + // AfterSelection, InsideSelection, and MultiSelection + + if (state?.key?.pos <= anchor && state?.valueEnd?.pos >= anchor) { + anchorPath = parseJSONPointer(pointer) + } + + if (state?.key?.pos <= head && state?.valueEnd?.pos >= head) { + focusPath = parseJSONPointer(pointer) + } + } + + return createMultiSelection(anchorPath, focusPath) + } catch { + return null + } +} + /** * Convert a JSON object, array, or value to another type * If it cannot be converted, an error is thrown