@coze-editor/code-language-json 0.1.0-alpha.37c297 → 0.1.0-alpha.577152
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/index.js +4 -33
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -43
- package/dist/index.js.map +1 -1
- package/package.json +5 -6
package/dist/esm/index.js
CHANGED
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
DiagnosticSeverity
|
|
7
7
|
} from "vscode-json-languageservice";
|
|
8
8
|
import { Text } from "text-mapping";
|
|
9
|
-
import Fuse from "fuse.js";
|
|
10
9
|
import { v4 as uuid } from "@lukeed/uuid";
|
|
11
10
|
import { parse } from "@coze-editor/parser-json";
|
|
12
11
|
import {
|
|
@@ -61,6 +60,7 @@ var JSONLanguageService = class {
|
|
|
61
60
|
}
|
|
62
61
|
languageService;
|
|
63
62
|
schemasConfigurations = [];
|
|
63
|
+
triggerCharacters = [":", '"', "'"];
|
|
64
64
|
configure() {
|
|
65
65
|
this.languageService.configure({
|
|
66
66
|
allowComments: false,
|
|
@@ -200,16 +200,7 @@ var JSONLanguageService = class {
|
|
|
200
200
|
}
|
|
201
201
|
async doComplete(context) {
|
|
202
202
|
const { textDocument } = context;
|
|
203
|
-
const text = textDocument.getText();
|
|
204
203
|
const { offset } = context;
|
|
205
|
-
const letterBefore = text.slice(
|
|
206
|
-
Math.max(0, offset - 1),
|
|
207
|
-
Math.min(text.length, offset)
|
|
208
|
-
);
|
|
209
|
-
const triggerCharacters = [":", '"', "'"];
|
|
210
|
-
if (!triggerCharacters.includes(letterBefore) && !/^\w+$/.test(letterBefore)) {
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
204
|
const { line, character } = textDocument.positionAt(offset);
|
|
214
205
|
const jsonDocument = this.parseJSONDocument(textDocument);
|
|
215
206
|
if (!jsonDocument) {
|
|
@@ -226,30 +217,10 @@ var JSONLanguageService = class {
|
|
|
226
217
|
if (!completionResult || !completionResult.items.length) {
|
|
227
218
|
return;
|
|
228
219
|
}
|
|
229
|
-
|
|
230
|
-
threshold: 0,
|
|
231
|
-
// allow matches appear in any place in target string
|
|
232
|
-
ignoreLocation: true,
|
|
233
|
-
keys: ["label"]
|
|
234
|
-
});
|
|
235
|
-
let i = context.offset - 1;
|
|
236
|
-
let query = "";
|
|
237
|
-
const content = context.textDocument.getText();
|
|
238
|
-
while (i >= 0) {
|
|
239
|
-
const char = content.slice(i, i + 1);
|
|
240
|
-
if (char === "\n") {
|
|
241
|
-
break;
|
|
242
|
-
}
|
|
243
|
-
if (triggerCharacters.includes(char) && i + 1 <= context.offset) {
|
|
244
|
-
query = content.slice(i + 1, context.offset);
|
|
245
|
-
break;
|
|
246
|
-
}
|
|
247
|
-
i--;
|
|
248
|
-
}
|
|
249
|
-
return query ? {
|
|
220
|
+
return {
|
|
250
221
|
isIncomplete: true,
|
|
251
|
-
items:
|
|
252
|
-
}
|
|
222
|
+
items: completionResult.items
|
|
223
|
+
};
|
|
253
224
|
}
|
|
254
225
|
async findLinks(context) {
|
|
255
226
|
const { textDocument } = context;
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/index.ts","../../src/json.ts"],"sourcesContent":["// Copyright (c) 2025 coze-dev\n// SPDX-License-Identifier: MIT\n\nimport { URI } from 'vscode-uri';\nimport {\n getLanguageService,\n TextDocument,\n type LanguageService,\n type JSONSchema,\n type CompletionList,\n type JSONDocument,\n DiagnosticSeverity,\n} from 'vscode-json-languageservice';\nimport { Text } from 'text-mapping';\nimport Fuse from 'fuse.js';\nimport { v4 as uuid } from '@lukeed/uuid';\nimport { parse } from '@coze-editor/parser-json';\nimport {\n type LanguageService as ILanguageService,\n type LanguageServiceOptions,\n type Link,\n textDocumentField,\n} from '@coze-editor/code-language-shared';\nimport {\n type ChangeSpec,\n type EditorState,\n type TransactionSpec,\n} from '@codemirror/state';\n\nimport { jsonLanguage } from './json';\n\ninterface SchemasConfiguration {\n uri: string;\n fileMatch: string[];\n schema: JSONSchema;\n}\n\ninterface ValidateOptions {\n schema?: JSONSchema;\n transform?: (text: Text) => Text;\n}\n\nfunction normalizeId(id: string) {\n // remove trailing '#', normalize drive capitalization\n try {\n return URI.parse(id).toString(true);\n } catch (e) {\n return id;\n }\n}\n\nfunction isNonNull<T>(value: T | undefined): value is T {\n return Boolean(value);\n}\n\nconst urlReg = /^(https?:\\/\\/|www\\.)[\\w-\\.]+\\.[\\w-\\.]+(\\/([\\S]+)?)?$/i;\n\nclass JSONLanguageService implements ILanguageService {\n protected languageService: LanguageService;\n private schemasConfigurations: SchemasConfiguration[] = [];\n\n constructor(public options: LanguageServiceOptions) {\n this.languageService = getLanguageService({});\n this.configure();\n }\n\n private configure() {\n this.languageService.configure({\n allowComments: false,\n schemas: this.schemasConfigurations.map(e => ({\n fileMatch: e.fileMatch,\n uri: normalizeId(e.uri),\n schema: e.schema,\n })),\n });\n }\n\n private parseJSONDocument(\n textDocument: TextDocument,\n ): JSONDocument | undefined {\n const text = parse(textDocument, { collectComments: true });\n if (!text.ast) {\n return;\n }\n\n const jsonDocument = this.languageService.newJSONDocument(\n text.ast,\n text.problems,\n );\n // @ts-expect-error comments type is not exported\n jsonDocument.comments = text.commentRanges;\n return jsonDocument;\n }\n\n public async validate(source: string, options?: ValidateOptions) {\n let text = new Text(source);\n\n if (typeof options?.transform === 'function') {\n text = options.transform(text);\n }\n\n const textDocument = TextDocument.create(\n `file:///anonymous-${uuid()}.json`,\n 'json',\n 0,\n text.toString(),\n );\n const jsonDocument = this.parseJSONDocument(textDocument);\n\n if (!jsonDocument) {\n return [];\n }\n\n const diagnostics = await this.languageService.doValidation(\n textDocument,\n jsonDocument,\n {},\n options?.schema,\n );\n\n return diagnostics\n .map(e => {\n const from = textDocument.offsetAt(e.range.start);\n const to = textDocument.offsetAt(e.range.end);\n\n return {\n from,\n to,\n severity: e.severity,\n message: e.message,\n };\n })\n .map(e => {\n const oRange = text.originalRangeFor({\n from: e.from,\n to: e.to,\n });\n if (!oRange) {\n return;\n }\n return {\n ...e,\n ...oRange,\n };\n })\n .filter(isNonNull);\n }\n\n public configureSchemas(\n config: SchemasConfiguration | SchemasConfiguration[],\n ) {\n let configs: SchemasConfiguration[] = [];\n configs = configs.concat(config);\n\n configs.forEach(c => {\n const match = this.schemasConfigurations.find(e => e.uri === c.uri);\n if (match) {\n match.schema = c.schema;\n match.fileMatch = c.fileMatch;\n } else {\n this.schemasConfigurations.push(c);\n }\n });\n\n this.configure();\n }\n\n public deleteSchemas(uri: string | string[]) {\n let allURI: string[] = [];\n allURI = allURI.concat(uri);\n\n this.schemasConfigurations = this.schemasConfigurations.filter(\n c => !allURI.includes(c.uri),\n );\n\n this.configure();\n }\n\n public async doValidation(\n context: Parameters<NonNullable<ILanguageService['doValidation']>>[0],\n ) {\n const { textDocument } = context;\n const jsonDocument = this.parseJSONDocument(textDocument);\n\n if (!jsonDocument) {\n return [];\n }\n\n const diagnostics = await this.languageService.doValidation(\n textDocument,\n jsonDocument,\n );\n\n return diagnostics.map(e => {\n const from = textDocument.offsetAt(e.range.start);\n const to = textDocument.offsetAt(e.range.end);\n\n return {\n from,\n to,\n severity: 'error' as const,\n message: e.message,\n };\n });\n }\n\n async format(\n state: EditorState,\n options?: { tabSize: number },\n ): Promise<TransactionSpec> {\n const { textDocument, originalRangeFor } = state.field(textDocumentField);\n\n const edits = this.languageService.format(\n textDocument,\n // @ts-expect-error range can be undefined\n undefined,\n {\n tabSize: options?.tabSize ?? 4,\n insertSpaces: true,\n insertFinalNewline: false,\n },\n );\n\n const changes = edits\n .map(edit => {\n const range = originalRangeFor({\n from: textDocument.offsetAt(edit.range.start),\n to: textDocument.offsetAt(edit.range.end),\n });\n if (range) {\n return {\n ...range,\n insert: edit.newText,\n };\n }\n })\n .filter(v => isChangeDesc(v));\n\n return {\n changes,\n };\n }\n\n public async doComplete(\n context: Parameters<NonNullable<ILanguageService['doComplete']>>[0],\n ): Promise<CompletionList | null | undefined> {\n const { textDocument } = context;\n const text = textDocument.getText();\n const { offset } = context;\n\n const letterBefore = text.slice(\n Math.max(0, offset - 1),\n Math.min(text.length, offset),\n );\n\n const triggerCharacters = [':', '\"', \"'\"];\n if (\n !triggerCharacters.includes(letterBefore) &&\n !/^\\w+$/.test(letterBefore)\n ) {\n return;\n }\n\n const { line, character } = textDocument.positionAt(offset);\n\n const jsonDocument = this.parseJSONDocument(textDocument);\n\n if (!jsonDocument) {\n return;\n }\n\n const completionResult = await this.languageService.doComplete(\n textDocument,\n {\n line,\n character,\n },\n jsonDocument,\n );\n\n if (!completionResult || !completionResult.items.length) {\n return;\n }\n\n const fuse = new Fuse(completionResult.items, {\n threshold: 0,\n // allow matches appear in any place in target string\n ignoreLocation: true,\n keys: ['label'],\n });\n\n let i = context.offset - 1;\n let query = '';\n\n const content = context.textDocument.getText();\n\n while (i >= 0) {\n const char = content.slice(i, i + 1);\n\n if (char === '\\n') {\n break;\n }\n\n if (triggerCharacters.includes(char) && i + 1 <= context.offset) {\n query = content.slice(i + 1, context.offset);\n break;\n }\n i--;\n }\n\n // cannot use validFor here as range changes during filtering\n return query\n ? {\n isIncomplete: true,\n items: fuse.search(query).map(v => v.item),\n }\n : completionResult;\n }\n\n async findLinks(\n context: Parameters<NonNullable<ILanguageService['findLinks']>>[0],\n ) {\n const { textDocument } = context;\n const doc = this.parseJSONDocument(textDocument);\n\n if (!doc) {\n return [];\n }\n\n const links: Link[] = [];\n\n // @ts-expect-error doc.visit is not exposed but indeed exists\n doc.visit(node => {\n if (node.type === 'string' && urlReg.test(node.value)) {\n const range = {\n from: node.offset + 1,\n to: node.offset + node.length - 1,\n };\n\n links.push({\n target: node.value,\n range,\n });\n }\n return true;\n });\n\n return links;\n }\n}\n\nfunction isChangeDesc(v: unknown): v is ChangeSpec {\n return Boolean(v);\n}\n\nconst jsonLanguageService = new JSONLanguageService({\n lintDelay: 0,\n});\n\nconst json = {\n language: jsonLanguage,\n languageService: jsonLanguageService,\n};\n\nexport { json, jsonLanguage, jsonLanguageService, JSONLanguageService };\n\nexport { DiagnosticSeverity, Text };\n","// Copyright (c) 2025 coze-dev\n// SPDX-License-Identifier: MIT\n\nimport { parser } from '@coze-editor/lezer-parser-json';\nimport {\n continuedIndent,\n indentNodeProp,\n foldNodeProp,\n foldInside,\n LRLanguage,\n} from '@codemirror/language';\n\n/// A language provider that provides JSON parsing.\nexport const jsonLanguage = LRLanguage.define({\n name: 'json',\n parser: parser.configure({\n props: [\n indentNodeProp.add({\n Object: continuedIndent({ except: /^\\s*\\}/ }),\n Array: continuedIndent({ except: /^\\s*\\]/ }),\n }),\n foldNodeProp.add({\n 'Object Array': foldInside,\n }),\n ],\n }),\n languageData: {\n closeBrackets: { brackets: ['[', '{', '\"', \"'\"] },\n\n indentOnInput: /^\\s*[\\}\\]]$/,\n },\n});\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/index.ts","../../src/json.ts"],"sourcesContent":["// Copyright (c) 2025 coze-dev\n// SPDX-License-Identifier: MIT\n\nimport { URI } from 'vscode-uri';\nimport {\n getLanguageService,\n TextDocument,\n type LanguageService,\n type JSONSchema,\n type CompletionList,\n type JSONDocument,\n DiagnosticSeverity,\n} from 'vscode-json-languageservice';\nimport { Text } from 'text-mapping';\nimport { v4 as uuid } from '@lukeed/uuid';\nimport { parse } from '@coze-editor/parser-json';\nimport {\n type LanguageService as ILanguageService,\n type LanguageServiceOptions,\n type Link,\n textDocumentField,\n} from '@coze-editor/code-language-shared';\nimport {\n type ChangeSpec,\n type EditorState,\n type TransactionSpec,\n} from '@codemirror/state';\n\nimport { jsonLanguage } from './json';\n\ninterface SchemasConfiguration {\n uri: string;\n fileMatch: string[];\n schema: JSONSchema;\n}\n\ninterface ValidateOptions {\n schema?: JSONSchema;\n transform?: (text: Text) => Text;\n}\n\nfunction normalizeId(id: string) {\n // remove trailing '#', normalize drive capitalization\n try {\n return URI.parse(id).toString(true);\n } catch (e) {\n return id;\n }\n}\n\nfunction isNonNull<T>(value: T | undefined): value is T {\n return Boolean(value);\n}\n\nconst urlReg = /^(https?:\\/\\/|www\\.)[\\w-\\.]+\\.[\\w-\\.]+(\\/([\\S]+)?)?$/i;\n\nclass JSONLanguageService implements ILanguageService {\n protected languageService: LanguageService;\n private schemasConfigurations: SchemasConfiguration[] = [];\n public triggerCharacters = [':', '\"', \"'\"];\n\n constructor(public options: LanguageServiceOptions) {\n this.languageService = getLanguageService({});\n this.configure();\n }\n\n private configure() {\n this.languageService.configure({\n allowComments: false,\n schemas: this.schemasConfigurations.map(e => ({\n fileMatch: e.fileMatch,\n uri: normalizeId(e.uri),\n schema: e.schema,\n })),\n });\n }\n\n private parseJSONDocument(\n textDocument: TextDocument,\n ): JSONDocument | undefined {\n const text = parse(textDocument, { collectComments: true });\n if (!text.ast) {\n return;\n }\n\n const jsonDocument = this.languageService.newJSONDocument(\n text.ast,\n text.problems,\n );\n // @ts-expect-error comments type is not exported\n jsonDocument.comments = text.commentRanges;\n return jsonDocument;\n }\n\n public async validate(source: string, options?: ValidateOptions) {\n let text = new Text(source);\n\n if (typeof options?.transform === 'function') {\n text = options.transform(text);\n }\n\n const textDocument = TextDocument.create(\n `file:///anonymous-${uuid()}.json`,\n 'json',\n 0,\n text.toString(),\n );\n const jsonDocument = this.parseJSONDocument(textDocument);\n\n if (!jsonDocument) {\n return [];\n }\n\n const diagnostics = await this.languageService.doValidation(\n textDocument,\n jsonDocument,\n {},\n options?.schema,\n );\n\n return diagnostics\n .map(e => {\n const from = textDocument.offsetAt(e.range.start);\n const to = textDocument.offsetAt(e.range.end);\n\n return {\n from,\n to,\n severity: e.severity,\n message: e.message,\n };\n })\n .map(e => {\n const oRange = text.originalRangeFor({\n from: e.from,\n to: e.to,\n });\n if (!oRange) {\n return;\n }\n return {\n ...e,\n ...oRange,\n };\n })\n .filter(isNonNull);\n }\n\n public configureSchemas(\n config: SchemasConfiguration | SchemasConfiguration[],\n ) {\n let configs: SchemasConfiguration[] = [];\n configs = configs.concat(config);\n\n configs.forEach(c => {\n const match = this.schemasConfigurations.find(e => e.uri === c.uri);\n if (match) {\n match.schema = c.schema;\n match.fileMatch = c.fileMatch;\n } else {\n this.schemasConfigurations.push(c);\n }\n });\n\n this.configure();\n }\n\n public deleteSchemas(uri: string | string[]) {\n let allURI: string[] = [];\n allURI = allURI.concat(uri);\n\n this.schemasConfigurations = this.schemasConfigurations.filter(\n c => !allURI.includes(c.uri),\n );\n\n this.configure();\n }\n\n public async doValidation(\n context: Parameters<NonNullable<ILanguageService['doValidation']>>[0],\n ) {\n const { textDocument } = context;\n const jsonDocument = this.parseJSONDocument(textDocument);\n\n if (!jsonDocument) {\n return [];\n }\n\n const diagnostics = await this.languageService.doValidation(\n textDocument,\n jsonDocument,\n );\n\n return diagnostics.map(e => {\n const from = textDocument.offsetAt(e.range.start);\n const to = textDocument.offsetAt(e.range.end);\n\n return {\n from,\n to,\n severity: 'error' as const,\n message: e.message,\n };\n });\n }\n\n async format(\n state: EditorState,\n options?: { tabSize: number },\n ): Promise<TransactionSpec> {\n const { textDocument, originalRangeFor } = state.field(textDocumentField);\n\n const edits = this.languageService.format(\n textDocument,\n // @ts-expect-error range can be undefined\n undefined,\n {\n tabSize: options?.tabSize ?? 4,\n insertSpaces: true,\n insertFinalNewline: false,\n },\n );\n\n const changes = edits\n .map(edit => {\n const range = originalRangeFor({\n from: textDocument.offsetAt(edit.range.start),\n to: textDocument.offsetAt(edit.range.end),\n });\n if (range) {\n return {\n ...range,\n insert: edit.newText,\n };\n }\n })\n .filter(v => isChangeDesc(v));\n\n return {\n changes,\n };\n }\n\n public async doComplete(\n context: Parameters<NonNullable<ILanguageService['doComplete']>>[0],\n ): Promise<CompletionList | null | undefined> {\n const { textDocument } = context;\n const { offset } = context;\n\n const { line, character } = textDocument.positionAt(offset);\n\n const jsonDocument = this.parseJSONDocument(textDocument);\n\n if (!jsonDocument) {\n return;\n }\n\n const completionResult = await this.languageService.doComplete(\n textDocument,\n {\n line,\n character,\n },\n jsonDocument,\n );\n\n if (!completionResult || !completionResult.items.length) {\n return;\n }\n\n return {\n isIncomplete: true,\n items: completionResult.items,\n };\n }\n\n async findLinks(\n context: Parameters<NonNullable<ILanguageService['findLinks']>>[0],\n ) {\n const { textDocument } = context;\n const doc = this.parseJSONDocument(textDocument);\n\n if (!doc) {\n return [];\n }\n\n const links: Link[] = [];\n\n // @ts-expect-error doc.visit is not exposed but indeed exists\n doc.visit(node => {\n if (node.type === 'string' && urlReg.test(node.value)) {\n const range = {\n from: node.offset + 1,\n to: node.offset + node.length - 1,\n };\n\n links.push({\n target: node.value,\n range,\n });\n }\n return true;\n });\n\n return links;\n }\n}\n\nfunction isChangeDesc(v: unknown): v is ChangeSpec {\n return Boolean(v);\n}\n\nconst jsonLanguageService = new JSONLanguageService({\n lintDelay: 0,\n});\n\nconst json = {\n language: jsonLanguage,\n languageService: jsonLanguageService,\n};\n\nexport { json, jsonLanguage, jsonLanguageService, JSONLanguageService };\n\nexport { DiagnosticSeverity, Text };\n","// Copyright (c) 2025 coze-dev\n// SPDX-License-Identifier: MIT\n\nimport { parser } from '@coze-editor/lezer-parser-json';\nimport {\n continuedIndent,\n indentNodeProp,\n foldNodeProp,\n foldInside,\n LRLanguage,\n} from '@codemirror/language';\n\n/// A language provider that provides JSON parsing.\nexport const jsonLanguage = LRLanguage.define({\n name: 'json',\n parser: parser.configure({\n props: [\n indentNodeProp.add({\n Object: continuedIndent({ except: /^\\s*\\}/ }),\n Array: continuedIndent({ except: /^\\s*\\]/ }),\n }),\n foldNodeProp.add({\n 'Object Array': foldInside,\n }),\n ],\n }),\n languageData: {\n closeBrackets: { brackets: ['[', '{', '\"', \"'\"] },\n\n indentOnInput: /^\\s*[\\}\\]]$/,\n },\n});\n"],"mappings":"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","names":[]}
|
package/dist/index.d.mts
CHANGED
|
@@ -22,6 +22,7 @@ declare class JSONLanguageService implements LanguageService {
|
|
|
22
22
|
options: LanguageServiceOptions;
|
|
23
23
|
protected languageService: LanguageService$1;
|
|
24
24
|
private schemasConfigurations;
|
|
25
|
+
triggerCharacters: string[];
|
|
25
26
|
constructor(options: LanguageServiceOptions);
|
|
26
27
|
private configure;
|
|
27
28
|
private parseJSONDocument;
|
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ declare class JSONLanguageService implements LanguageService {
|
|
|
22
22
|
options: LanguageServiceOptions;
|
|
23
23
|
protected languageService: LanguageService$1;
|
|
24
24
|
private schemasConfigurations;
|
|
25
|
+
triggerCharacters: string[];
|
|
25
26
|
constructor(options: LanguageServiceOptions);
|
|
26
27
|
private configure;
|
|
27
28
|
private parseJSONDocument;
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
var __create = Object.create;
|
|
2
1
|
var __defProp = Object.defineProperty;
|
|
3
2
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
6
4
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
5
|
var __export = (target, all) => {
|
|
8
6
|
for (var name in all)
|
|
@@ -16,14 +14,6 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
14
|
}
|
|
17
15
|
return to;
|
|
18
16
|
};
|
|
19
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
-
mod
|
|
26
|
-
));
|
|
27
17
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
18
|
|
|
29
19
|
// src/index.ts
|
|
@@ -40,7 +30,6 @@ module.exports = __toCommonJS(index_exports);
|
|
|
40
30
|
var import_vscode_uri = require("vscode-uri");
|
|
41
31
|
var import_vscode_json_languageservice = require("vscode-json-languageservice");
|
|
42
32
|
var import_text_mapping = require("text-mapping");
|
|
43
|
-
var import_fuse = __toESM(require("fuse.js"));
|
|
44
33
|
var import_uuid = require("@lukeed/uuid");
|
|
45
34
|
var import_parser_json = require("@coze-editor/parser-json");
|
|
46
35
|
var import_code_language_shared = require("@coze-editor/code-language-shared");
|
|
@@ -87,6 +76,7 @@ var JSONLanguageService = class {
|
|
|
87
76
|
}
|
|
88
77
|
languageService;
|
|
89
78
|
schemasConfigurations = [];
|
|
79
|
+
triggerCharacters = [":", '"', "'"];
|
|
90
80
|
configure() {
|
|
91
81
|
this.languageService.configure({
|
|
92
82
|
allowComments: false,
|
|
@@ -226,16 +216,7 @@ var JSONLanguageService = class {
|
|
|
226
216
|
}
|
|
227
217
|
async doComplete(context) {
|
|
228
218
|
const { textDocument } = context;
|
|
229
|
-
const text = textDocument.getText();
|
|
230
219
|
const { offset } = context;
|
|
231
|
-
const letterBefore = text.slice(
|
|
232
|
-
Math.max(0, offset - 1),
|
|
233
|
-
Math.min(text.length, offset)
|
|
234
|
-
);
|
|
235
|
-
const triggerCharacters = [":", '"', "'"];
|
|
236
|
-
if (!triggerCharacters.includes(letterBefore) && !/^\w+$/.test(letterBefore)) {
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
220
|
const { line, character } = textDocument.positionAt(offset);
|
|
240
221
|
const jsonDocument = this.parseJSONDocument(textDocument);
|
|
241
222
|
if (!jsonDocument) {
|
|
@@ -252,30 +233,10 @@ var JSONLanguageService = class {
|
|
|
252
233
|
if (!completionResult || !completionResult.items.length) {
|
|
253
234
|
return;
|
|
254
235
|
}
|
|
255
|
-
|
|
256
|
-
threshold: 0,
|
|
257
|
-
// allow matches appear in any place in target string
|
|
258
|
-
ignoreLocation: true,
|
|
259
|
-
keys: ["label"]
|
|
260
|
-
});
|
|
261
|
-
let i = context.offset - 1;
|
|
262
|
-
let query = "";
|
|
263
|
-
const content = context.textDocument.getText();
|
|
264
|
-
while (i >= 0) {
|
|
265
|
-
const char = content.slice(i, i + 1);
|
|
266
|
-
if (char === "\n") {
|
|
267
|
-
break;
|
|
268
|
-
}
|
|
269
|
-
if (triggerCharacters.includes(char) && i + 1 <= context.offset) {
|
|
270
|
-
query = content.slice(i + 1, context.offset);
|
|
271
|
-
break;
|
|
272
|
-
}
|
|
273
|
-
i--;
|
|
274
|
-
}
|
|
275
|
-
return query ? {
|
|
236
|
+
return {
|
|
276
237
|
isIncomplete: true,
|
|
277
|
-
items:
|
|
278
|
-
}
|
|
238
|
+
items: completionResult.items
|
|
239
|
+
};
|
|
279
240
|
}
|
|
280
241
|
async findLinks(context) {
|
|
281
242
|
const { textDocument } = context;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/json.ts"],"sourcesContent":["// Copyright (c) 2025 coze-dev\n// SPDX-License-Identifier: MIT\n\nimport { URI } from 'vscode-uri';\nimport {\n getLanguageService,\n TextDocument,\n type LanguageService,\n type JSONSchema,\n type CompletionList,\n type JSONDocument,\n DiagnosticSeverity,\n} from 'vscode-json-languageservice';\nimport { Text } from 'text-mapping';\nimport Fuse from 'fuse.js';\nimport { v4 as uuid } from '@lukeed/uuid';\nimport { parse } from '@coze-editor/parser-json';\nimport {\n type LanguageService as ILanguageService,\n type LanguageServiceOptions,\n type Link,\n textDocumentField,\n} from '@coze-editor/code-language-shared';\nimport {\n type ChangeSpec,\n type EditorState,\n type TransactionSpec,\n} from '@codemirror/state';\n\nimport { jsonLanguage } from './json';\n\ninterface SchemasConfiguration {\n uri: string;\n fileMatch: string[];\n schema: JSONSchema;\n}\n\ninterface ValidateOptions {\n schema?: JSONSchema;\n transform?: (text: Text) => Text;\n}\n\nfunction normalizeId(id: string) {\n // remove trailing '#', normalize drive capitalization\n try {\n return URI.parse(id).toString(true);\n } catch (e) {\n return id;\n }\n}\n\nfunction isNonNull<T>(value: T | undefined): value is T {\n return Boolean(value);\n}\n\nconst urlReg = /^(https?:\\/\\/|www\\.)[\\w-\\.]+\\.[\\w-\\.]+(\\/([\\S]+)?)?$/i;\n\nclass JSONLanguageService implements ILanguageService {\n protected languageService: LanguageService;\n private schemasConfigurations: SchemasConfiguration[] = [];\n\n constructor(public options: LanguageServiceOptions) {\n this.languageService = getLanguageService({});\n this.configure();\n }\n\n private configure() {\n this.languageService.configure({\n allowComments: false,\n schemas: this.schemasConfigurations.map(e => ({\n fileMatch: e.fileMatch,\n uri: normalizeId(e.uri),\n schema: e.schema,\n })),\n });\n }\n\n private parseJSONDocument(\n textDocument: TextDocument,\n ): JSONDocument | undefined {\n const text = parse(textDocument, { collectComments: true });\n if (!text.ast) {\n return;\n }\n\n const jsonDocument = this.languageService.newJSONDocument(\n text.ast,\n text.problems,\n );\n // @ts-expect-error comments type is not exported\n jsonDocument.comments = text.commentRanges;\n return jsonDocument;\n }\n\n public async validate(source: string, options?: ValidateOptions) {\n let text = new Text(source);\n\n if (typeof options?.transform === 'function') {\n text = options.transform(text);\n }\n\n const textDocument = TextDocument.create(\n `file:///anonymous-${uuid()}.json`,\n 'json',\n 0,\n text.toString(),\n );\n const jsonDocument = this.parseJSONDocument(textDocument);\n\n if (!jsonDocument) {\n return [];\n }\n\n const diagnostics = await this.languageService.doValidation(\n textDocument,\n jsonDocument,\n {},\n options?.schema,\n );\n\n return diagnostics\n .map(e => {\n const from = textDocument.offsetAt(e.range.start);\n const to = textDocument.offsetAt(e.range.end);\n\n return {\n from,\n to,\n severity: e.severity,\n message: e.message,\n };\n })\n .map(e => {\n const oRange = text.originalRangeFor({\n from: e.from,\n to: e.to,\n });\n if (!oRange) {\n return;\n }\n return {\n ...e,\n ...oRange,\n };\n })\n .filter(isNonNull);\n }\n\n public configureSchemas(\n config: SchemasConfiguration | SchemasConfiguration[],\n ) {\n let configs: SchemasConfiguration[] = [];\n configs = configs.concat(config);\n\n configs.forEach(c => {\n const match = this.schemasConfigurations.find(e => e.uri === c.uri);\n if (match) {\n match.schema = c.schema;\n match.fileMatch = c.fileMatch;\n } else {\n this.schemasConfigurations.push(c);\n }\n });\n\n this.configure();\n }\n\n public deleteSchemas(uri: string | string[]) {\n let allURI: string[] = [];\n allURI = allURI.concat(uri);\n\n this.schemasConfigurations = this.schemasConfigurations.filter(\n c => !allURI.includes(c.uri),\n );\n\n this.configure();\n }\n\n public async doValidation(\n context: Parameters<NonNullable<ILanguageService['doValidation']>>[0],\n ) {\n const { textDocument } = context;\n const jsonDocument = this.parseJSONDocument(textDocument);\n\n if (!jsonDocument) {\n return [];\n }\n\n const diagnostics = await this.languageService.doValidation(\n textDocument,\n jsonDocument,\n );\n\n return diagnostics.map(e => {\n const from = textDocument.offsetAt(e.range.start);\n const to = textDocument.offsetAt(e.range.end);\n\n return {\n from,\n to,\n severity: 'error' as const,\n message: e.message,\n };\n });\n }\n\n async format(\n state: EditorState,\n options?: { tabSize: number },\n ): Promise<TransactionSpec> {\n const { textDocument, originalRangeFor } = state.field(textDocumentField);\n\n const edits = this.languageService.format(\n textDocument,\n // @ts-expect-error range can be undefined\n undefined,\n {\n tabSize: options?.tabSize ?? 4,\n insertSpaces: true,\n insertFinalNewline: false,\n },\n );\n\n const changes = edits\n .map(edit => {\n const range = originalRangeFor({\n from: textDocument.offsetAt(edit.range.start),\n to: textDocument.offsetAt(edit.range.end),\n });\n if (range) {\n return {\n ...range,\n insert: edit.newText,\n };\n }\n })\n .filter(v => isChangeDesc(v));\n\n return {\n changes,\n };\n }\n\n public async doComplete(\n context: Parameters<NonNullable<ILanguageService['doComplete']>>[0],\n ): Promise<CompletionList | null | undefined> {\n const { textDocument } = context;\n const text = textDocument.getText();\n const { offset } = context;\n\n const letterBefore = text.slice(\n Math.max(0, offset - 1),\n Math.min(text.length, offset),\n );\n\n const triggerCharacters = [':', '\"', \"'\"];\n if (\n !triggerCharacters.includes(letterBefore) &&\n !/^\\w+$/.test(letterBefore)\n ) {\n return;\n }\n\n const { line, character } = textDocument.positionAt(offset);\n\n const jsonDocument = this.parseJSONDocument(textDocument);\n\n if (!jsonDocument) {\n return;\n }\n\n const completionResult = await this.languageService.doComplete(\n textDocument,\n {\n line,\n character,\n },\n jsonDocument,\n );\n\n if (!completionResult || !completionResult.items.length) {\n return;\n }\n\n const fuse = new Fuse(completionResult.items, {\n threshold: 0,\n // allow matches appear in any place in target string\n ignoreLocation: true,\n keys: ['label'],\n });\n\n let i = context.offset - 1;\n let query = '';\n\n const content = context.textDocument.getText();\n\n while (i >= 0) {\n const char = content.slice(i, i + 1);\n\n if (char === '\\n') {\n break;\n }\n\n if (triggerCharacters.includes(char) && i + 1 <= context.offset) {\n query = content.slice(i + 1, context.offset);\n break;\n }\n i--;\n }\n\n // cannot use validFor here as range changes during filtering\n return query\n ? {\n isIncomplete: true,\n items: fuse.search(query).map(v => v.item),\n }\n : completionResult;\n }\n\n async findLinks(\n context: Parameters<NonNullable<ILanguageService['findLinks']>>[0],\n ) {\n const { textDocument } = context;\n const doc = this.parseJSONDocument(textDocument);\n\n if (!doc) {\n return [];\n }\n\n const links: Link[] = [];\n\n // @ts-expect-error doc.visit is not exposed but indeed exists\n doc.visit(node => {\n if (node.type === 'string' && urlReg.test(node.value)) {\n const range = {\n from: node.offset + 1,\n to: node.offset + node.length - 1,\n };\n\n links.push({\n target: node.value,\n range,\n });\n }\n return true;\n });\n\n return links;\n }\n}\n\nfunction isChangeDesc(v: unknown): v is ChangeSpec {\n return Boolean(v);\n}\n\nconst jsonLanguageService = new JSONLanguageService({\n lintDelay: 0,\n});\n\nconst json = {\n language: jsonLanguage,\n languageService: jsonLanguageService,\n};\n\nexport { json, jsonLanguage, jsonLanguageService, JSONLanguageService };\n\nexport { DiagnosticSeverity, Text };\n","// Copyright (c) 2025 coze-dev\n// SPDX-License-Identifier: MIT\n\nimport { parser } from '@coze-editor/lezer-parser-json';\nimport {\n continuedIndent,\n indentNodeProp,\n foldNodeProp,\n foldInside,\n LRLanguage,\n} from '@codemirror/language';\n\n/// A language provider that provides JSON parsing.\nexport const jsonLanguage = LRLanguage.define({\n name: 'json',\n parser: parser.configure({\n props: [\n indentNodeProp.add({\n Object: continuedIndent({ except: /^\\s*\\}/ }),\n Array: continuedIndent({ except: /^\\s*\\]/ }),\n }),\n foldNodeProp.add({\n 'Object Array': foldInside,\n }),\n ],\n }),\n languageData: {\n closeBrackets: { brackets: ['[', '{', '\"', \"'\"] },\n\n indentOnInput: /^\\s*[\\}\\]]$/,\n },\n});\n"],"mappings":"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","names":["uuid","Fuse"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/json.ts"],"sourcesContent":["// Copyright (c) 2025 coze-dev\n// SPDX-License-Identifier: MIT\n\nimport { URI } from 'vscode-uri';\nimport {\n getLanguageService,\n TextDocument,\n type LanguageService,\n type JSONSchema,\n type CompletionList,\n type JSONDocument,\n DiagnosticSeverity,\n} from 'vscode-json-languageservice';\nimport { Text } from 'text-mapping';\nimport { v4 as uuid } from '@lukeed/uuid';\nimport { parse } from '@coze-editor/parser-json';\nimport {\n type LanguageService as ILanguageService,\n type LanguageServiceOptions,\n type Link,\n textDocumentField,\n} from '@coze-editor/code-language-shared';\nimport {\n type ChangeSpec,\n type EditorState,\n type TransactionSpec,\n} from '@codemirror/state';\n\nimport { jsonLanguage } from './json';\n\ninterface SchemasConfiguration {\n uri: string;\n fileMatch: string[];\n schema: JSONSchema;\n}\n\ninterface ValidateOptions {\n schema?: JSONSchema;\n transform?: (text: Text) => Text;\n}\n\nfunction normalizeId(id: string) {\n // remove trailing '#', normalize drive capitalization\n try {\n return URI.parse(id).toString(true);\n } catch (e) {\n return id;\n }\n}\n\nfunction isNonNull<T>(value: T | undefined): value is T {\n return Boolean(value);\n}\n\nconst urlReg = /^(https?:\\/\\/|www\\.)[\\w-\\.]+\\.[\\w-\\.]+(\\/([\\S]+)?)?$/i;\n\nclass JSONLanguageService implements ILanguageService {\n protected languageService: LanguageService;\n private schemasConfigurations: SchemasConfiguration[] = [];\n public triggerCharacters = [':', '\"', \"'\"];\n\n constructor(public options: LanguageServiceOptions) {\n this.languageService = getLanguageService({});\n this.configure();\n }\n\n private configure() {\n this.languageService.configure({\n allowComments: false,\n schemas: this.schemasConfigurations.map(e => ({\n fileMatch: e.fileMatch,\n uri: normalizeId(e.uri),\n schema: e.schema,\n })),\n });\n }\n\n private parseJSONDocument(\n textDocument: TextDocument,\n ): JSONDocument | undefined {\n const text = parse(textDocument, { collectComments: true });\n if (!text.ast) {\n return;\n }\n\n const jsonDocument = this.languageService.newJSONDocument(\n text.ast,\n text.problems,\n );\n // @ts-expect-error comments type is not exported\n jsonDocument.comments = text.commentRanges;\n return jsonDocument;\n }\n\n public async validate(source: string, options?: ValidateOptions) {\n let text = new Text(source);\n\n if (typeof options?.transform === 'function') {\n text = options.transform(text);\n }\n\n const textDocument = TextDocument.create(\n `file:///anonymous-${uuid()}.json`,\n 'json',\n 0,\n text.toString(),\n );\n const jsonDocument = this.parseJSONDocument(textDocument);\n\n if (!jsonDocument) {\n return [];\n }\n\n const diagnostics = await this.languageService.doValidation(\n textDocument,\n jsonDocument,\n {},\n options?.schema,\n );\n\n return diagnostics\n .map(e => {\n const from = textDocument.offsetAt(e.range.start);\n const to = textDocument.offsetAt(e.range.end);\n\n return {\n from,\n to,\n severity: e.severity,\n message: e.message,\n };\n })\n .map(e => {\n const oRange = text.originalRangeFor({\n from: e.from,\n to: e.to,\n });\n if (!oRange) {\n return;\n }\n return {\n ...e,\n ...oRange,\n };\n })\n .filter(isNonNull);\n }\n\n public configureSchemas(\n config: SchemasConfiguration | SchemasConfiguration[],\n ) {\n let configs: SchemasConfiguration[] = [];\n configs = configs.concat(config);\n\n configs.forEach(c => {\n const match = this.schemasConfigurations.find(e => e.uri === c.uri);\n if (match) {\n match.schema = c.schema;\n match.fileMatch = c.fileMatch;\n } else {\n this.schemasConfigurations.push(c);\n }\n });\n\n this.configure();\n }\n\n public deleteSchemas(uri: string | string[]) {\n let allURI: string[] = [];\n allURI = allURI.concat(uri);\n\n this.schemasConfigurations = this.schemasConfigurations.filter(\n c => !allURI.includes(c.uri),\n );\n\n this.configure();\n }\n\n public async doValidation(\n context: Parameters<NonNullable<ILanguageService['doValidation']>>[0],\n ) {\n const { textDocument } = context;\n const jsonDocument = this.parseJSONDocument(textDocument);\n\n if (!jsonDocument) {\n return [];\n }\n\n const diagnostics = await this.languageService.doValidation(\n textDocument,\n jsonDocument,\n );\n\n return diagnostics.map(e => {\n const from = textDocument.offsetAt(e.range.start);\n const to = textDocument.offsetAt(e.range.end);\n\n return {\n from,\n to,\n severity: 'error' as const,\n message: e.message,\n };\n });\n }\n\n async format(\n state: EditorState,\n options?: { tabSize: number },\n ): Promise<TransactionSpec> {\n const { textDocument, originalRangeFor } = state.field(textDocumentField);\n\n const edits = this.languageService.format(\n textDocument,\n // @ts-expect-error range can be undefined\n undefined,\n {\n tabSize: options?.tabSize ?? 4,\n insertSpaces: true,\n insertFinalNewline: false,\n },\n );\n\n const changes = edits\n .map(edit => {\n const range = originalRangeFor({\n from: textDocument.offsetAt(edit.range.start),\n to: textDocument.offsetAt(edit.range.end),\n });\n if (range) {\n return {\n ...range,\n insert: edit.newText,\n };\n }\n })\n .filter(v => isChangeDesc(v));\n\n return {\n changes,\n };\n }\n\n public async doComplete(\n context: Parameters<NonNullable<ILanguageService['doComplete']>>[0],\n ): Promise<CompletionList | null | undefined> {\n const { textDocument } = context;\n const { offset } = context;\n\n const { line, character } = textDocument.positionAt(offset);\n\n const jsonDocument = this.parseJSONDocument(textDocument);\n\n if (!jsonDocument) {\n return;\n }\n\n const completionResult = await this.languageService.doComplete(\n textDocument,\n {\n line,\n character,\n },\n jsonDocument,\n );\n\n if (!completionResult || !completionResult.items.length) {\n return;\n }\n\n return {\n isIncomplete: true,\n items: completionResult.items,\n };\n }\n\n async findLinks(\n context: Parameters<NonNullable<ILanguageService['findLinks']>>[0],\n ) {\n const { textDocument } = context;\n const doc = this.parseJSONDocument(textDocument);\n\n if (!doc) {\n return [];\n }\n\n const links: Link[] = [];\n\n // @ts-expect-error doc.visit is not exposed but indeed exists\n doc.visit(node => {\n if (node.type === 'string' && urlReg.test(node.value)) {\n const range = {\n from: node.offset + 1,\n to: node.offset + node.length - 1,\n };\n\n links.push({\n target: node.value,\n range,\n });\n }\n return true;\n });\n\n return links;\n }\n}\n\nfunction isChangeDesc(v: unknown): v is ChangeSpec {\n return Boolean(v);\n}\n\nconst jsonLanguageService = new JSONLanguageService({\n lintDelay: 0,\n});\n\nconst json = {\n language: jsonLanguage,\n languageService: jsonLanguageService,\n};\n\nexport { json, jsonLanguage, jsonLanguageService, JSONLanguageService };\n\nexport { DiagnosticSeverity, Text };\n","// Copyright (c) 2025 coze-dev\n// SPDX-License-Identifier: MIT\n\nimport { parser } from '@coze-editor/lezer-parser-json';\nimport {\n continuedIndent,\n indentNodeProp,\n foldNodeProp,\n foldInside,\n LRLanguage,\n} from '@codemirror/language';\n\n/// A language provider that provides JSON parsing.\nexport const jsonLanguage = LRLanguage.define({\n name: 'json',\n parser: parser.configure({\n props: [\n indentNodeProp.add({\n Object: continuedIndent({ except: /^\\s*\\}/ }),\n Array: continuedIndent({ except: /^\\s*\\]/ }),\n }),\n foldNodeProp.add({\n 'Object Array': foldInside,\n }),\n ],\n }),\n languageData: {\n closeBrackets: { brackets: ['[', '{', '\"', \"'\"] },\n\n indentOnInput: /^\\s*[\\}\\]]$/,\n },\n});\n"],"mappings":"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","names":["uuid"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coze-editor/code-language-json",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.577152",
|
|
4
4
|
"description": "code-language-json",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "fengzilong",
|
|
@@ -23,12 +23,11 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@codemirror/autocomplete": "^6.18.0",
|
|
26
|
-
"@coze-editor/extension-lint": "0.1.0-alpha.
|
|
27
|
-
"@coze-editor/lezer-parser-json": "0.1.0-alpha.
|
|
28
|
-
"@coze-editor/parser-json": "0.1.0-alpha.
|
|
26
|
+
"@coze-editor/extension-lint": "0.1.0-alpha.577152",
|
|
27
|
+
"@coze-editor/lezer-parser-json": "0.1.0-alpha.577152",
|
|
28
|
+
"@coze-editor/parser-json": "0.1.0-alpha.577152",
|
|
29
29
|
"@lezer/json": "^1.0.2",
|
|
30
30
|
"@lukeed/uuid": "^2.0.1",
|
|
31
|
-
"fuse.js": "^7.0.0",
|
|
32
31
|
"text-mapping": "^1.0.1",
|
|
33
32
|
"vscode-json-languageservice": "^5.4.2",
|
|
34
33
|
"vscode-uri": "^3.0.8"
|
|
@@ -49,7 +48,7 @@
|
|
|
49
48
|
"@codemirror/language": "^6.0.0",
|
|
50
49
|
"@codemirror/state": "^6.4.1",
|
|
51
50
|
"@codemirror/view": "^6.26.1",
|
|
52
|
-
"@coze-editor/code-language-shared": "0.1.0-alpha.
|
|
51
|
+
"@coze-editor/code-language-shared": "0.1.0-alpha.577152"
|
|
53
52
|
},
|
|
54
53
|
"publishConfig": {
|
|
55
54
|
"access": "public",
|