Skip to content

Commit 6a6d6c0

Browse files
diyaayayjdesrosiers
authored andcommitted
added goto-defintions
changed feature Fixes file paths
1 parent 2c743dc commit 6a6d6c0

16 files changed

+386
-23
lines changed

language-server/package-lock.json

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

language-server/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
"jsonc-parser": "^3.2.1",
1919
"merge-anything": "^6.0.2",
2020
"vscode-languageserver": "^9.0.0",
21-
"vscode-languageserver-textdocument": "^1.0.8"
21+
"vscode-languageserver-textdocument": "^1.0.8",
22+
"vscode-uri": "^3.0.8"
2223
},
2324
"engines": {
2425
"node": ">=20.13.0"
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { some } from "@hyperjump/pact";
2+
import * as SchemaDocument from "../schema-document.js";
3+
import * as SchemaNode from "../schema-node.js";
4+
import { getSchemaDocument, getSchemaDocumentBySchemaUri } from "./schema-registry.js";
5+
import { keywordNameFor } from "../util.js";
6+
7+
/** @import { Feature } from "../build-server.js" */
8+
/** @import { SchemaNode as SchemaNodeType } from "../schema-node.js" */
9+
10+
/** @type Feature */
11+
export default {
12+
load(connection, documents) {
13+
const highlightBlockDialects = new Set([
14+
"http://json-schema.org/draft-04/schema",
15+
"http://json-schema.org/draft-06/schema",
16+
"http://json-schema.org/draft-07/schema"
17+
]);
18+
19+
/** @type (node: SchemaNodeType) => boolean */
20+
const isReference = (node) => {
21+
if (!node.dialectUri) {
22+
return false;
23+
}
24+
25+
if (highlightBlockDialects.has(node.dialectUri)) {
26+
// Legacy reference
27+
const legacyRefToken = keywordNameFor("https://json-schema.org/keyword/draft-04/ref", node.dialectUri);
28+
if (!node.parent || !node.parent.parent) {
29+
return false;
30+
}
31+
32+
const referenceNode = node.parent.parent;
33+
return some((keywordNode) => SchemaNode.value(keywordNode) === legacyRefToken, SchemaNode.keys(referenceNode));
34+
} else {
35+
// Regular reference
36+
if (!node.parent) {
37+
return false;
38+
}
39+
40+
const refToken = keywordNameFor("https://json-schema.org/keyword/ref", node.dialectUri);
41+
const keyword = SchemaNode.value(node.parent?.children[0]);
42+
return keyword === refToken;
43+
}
44+
};
45+
46+
connection.onDefinition(async ({ textDocument, position }) => {
47+
const document = documents.get(textDocument.uri);
48+
if (!document) {
49+
return [];
50+
}
51+
52+
const schemaDocument = await getSchemaDocument(connection, document);
53+
const offset = document.offsetAt(position);
54+
const node = SchemaDocument.findNodeAtOffset(schemaDocument, offset);
55+
56+
if (!node || !isReference(node)) {
57+
return [];
58+
}
59+
60+
const reference = SchemaNode.value(node);
61+
const targetSchema = SchemaNode.get(reference, node);
62+
63+
if (!targetSchema) {
64+
return [];
65+
}
66+
67+
const targetSchemaDocument = getSchemaDocumentBySchemaUri(targetSchema.baseUri);
68+
if (!targetSchemaDocument) {
69+
return [];
70+
}
71+
72+
const gotoDefinitions = [{
73+
uri: targetSchemaDocument.textDocument.uri,
74+
range: {
75+
start: targetSchemaDocument.textDocument.positionAt(targetSchema.offset),
76+
end: targetSchemaDocument.textDocument.positionAt(targetSchema.offset + targetSchema.textLength)
77+
}
78+
}];
79+
80+
return gotoDefinitions;
81+
});
82+
},
83+
84+
onInitialize() {
85+
return {
86+
definitionProvider: true
87+
};
88+
},
89+
90+
async onInitialized() {
91+
},
92+
93+
onShutdown() {
94+
}
95+
};
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
import { afterEach, beforeEach, describe, expect, test } from "vitest";
2+
import { DefinitionRequest } from "vscode-languageserver";
3+
import { TestClient } from "../test-client.js";
4+
import documentSettings from "./document-settings.js";
5+
import schemaRegistry from "./schema-registry.js";
6+
import workspace from "./workspace.js";
7+
import definitionFeature from "./definition.js";
8+
9+
import type { DocumentSettings } from "./document-settings.js";
10+
11+
12+
describe("Feature - Goto Definition", () => {
13+
let client: TestClient<DocumentSettings>;
14+
15+
beforeEach(async () => {
16+
client = new TestClient([
17+
workspace,
18+
documentSettings,
19+
schemaRegistry,
20+
definitionFeature
21+
]);
22+
await client.start();
23+
});
24+
25+
afterEach(async () => {
26+
await client.stop();
27+
});
28+
29+
test("no defintions", async () => {
30+
const documentUri = await client.openDocument("./subject.schema.json", `{}`);
31+
32+
const response = await client.sendRequest(DefinitionRequest.type, {
33+
textDocument: { uri: documentUri },
34+
position: {
35+
line: 0,
36+
character: 1
37+
}
38+
});
39+
40+
expect(response).to.eql([]);
41+
});
42+
43+
test("don't return definitions that do not match location", async () => {
44+
const documentUri = await client.openDocument("./subject.schema.json", `{
45+
"$schema": "https://json-schema.org/draft/2020-12/schema",
46+
"$ref": "#/$defs/locations",
47+
"$defs": {
48+
"names": {
49+
50+
},
51+
"locations": {
52+
53+
}
54+
},
55+
}`);
56+
57+
const response = await client.sendRequest(DefinitionRequest.type, {
58+
textDocument: { uri: documentUri },
59+
position: {
60+
line: 2,
61+
character: 11
62+
}
63+
});
64+
65+
expect(response).to.eql([{
66+
uri: documentUri,
67+
range: {
68+
start: { line: 7, character: 17 },
69+
end: { line: 9, character: 5 }
70+
}
71+
}]);
72+
});
73+
74+
test("match one reference", async () => {
75+
const documentUri = await client.openDocument("./subject.schema.json", `{
76+
"$schema":"https://json-schema.org/draft/2020-12/schema",
77+
"$ref": "#/$defs/names",
78+
"$defs":{
79+
"names": {
80+
81+
}
82+
},
83+
}`);
84+
85+
const response = await client.sendRequest(DefinitionRequest.type, {
86+
textDocument: { uri: documentUri },
87+
position: {
88+
line: 2,
89+
character: 20
90+
}
91+
});
92+
93+
expect(response).to.eql([
94+
{
95+
"uri": documentUri,
96+
"range": {
97+
"start": { "line": 4, "character": 13 },
98+
"end": { "line": 6, "character": 5 }
99+
}
100+
}
101+
]);
102+
});
103+
test("match one definition", async () => {
104+
const documentUri = await client.openDocument("./subject.schema.json", `{
105+
"$schema":"https://json-schema.org/draft/2020-12/schema",
106+
"$ref": "#/$defs/names",
107+
"$defs":{
108+
"names": {
109+
110+
}
111+
},
112+
}`);
113+
114+
const response = await client.sendRequest(DefinitionRequest.type, {
115+
textDocument: { uri: documentUri },
116+
position: {
117+
line: 2,
118+
character: 18
119+
}
120+
});
121+
122+
expect(response).to.eql([
123+
{
124+
"uri": documentUri,
125+
"range": {
126+
"start": { "line": 4, "character": 13 },
127+
"end": { "line": 6, "character": 5 }
128+
}
129+
}
130+
]);
131+
});
132+
133+
test("cross file definition", async () => {
134+
const documentUriA = await client.openDocument("./subjectA.schema.json", `{
135+
"$schema": "http://json-schema.org/draft-07/schema#",
136+
"definitions": {
137+
"person": {
138+
139+
}
140+
}
141+
}
142+
`);
143+
const documentUriB = await client.openDocument("./subjectB.schema.json", `{
144+
"$schema": "http://json-schema.org/draft-07/schema#",
145+
"$ref": "./subjectA.schema.json#/definitions/person"
146+
}
147+
`);
148+
149+
const response = await client.sendRequest(DefinitionRequest.type, {
150+
textDocument: { uri: documentUriB },
151+
position: {
152+
line: 2,
153+
character: 20
154+
}
155+
});
156+
157+
expect(response).to.eql([
158+
{
159+
"uri": documentUriA,
160+
"range": {
161+
"start": { "line": 3, "character": 14 },
162+
"end": { "line": 5, "character": 5 }
163+
}
164+
}
165+
]);
166+
});
167+
168+
test("match self identified externally", async () => {
169+
const documentUri = await client.openDocument("./subject.schema.json", `{
170+
"$schema":"http://json-schema.org/draft-07/schema#",
171+
"$ref": "https://example.com/schemas/two#/definitions/names",
172+
}`);
173+
174+
const documentUriB = await client.openDocument("./subjectB.schema.json", `{
175+
"$schema":"http://json-schema.org/draft-07/schema#",
176+
"$id": "https://example.com/schemas/two",
177+
"definitions":{
178+
"names": {
179+
180+
}
181+
}
182+
}`);
183+
184+
const response = await client.sendRequest(DefinitionRequest.type, {
185+
textDocument: { uri: documentUri },
186+
position: {
187+
line: 2,
188+
character: 37
189+
}
190+
});
191+
192+
expect(response).to.eql([
193+
{
194+
"uri": documentUriB,
195+
"range": {
196+
"start": { "line": 4, "character": 13 },
197+
"end": { "line": 6, "character": 5 }
198+
}
199+
}
200+
]);
201+
});
202+
203+
test("match self identified internally", async () => {
204+
const documentUri = await client.openDocument("./subject.schema.json", `{
205+
"$schema":"http://json-schema.org/draft-07/schema#",
206+
"$id": "https://example.com/person.json",
207+
"type": "object",
208+
"properties": {
209+
"names": { "$ref": "https://example.com/person.json" }
210+
}
211+
}`);
212+
213+
const response = await client.sendRequest(DefinitionRequest.type, {
214+
textDocument: { uri: documentUri },
215+
position: {
216+
line: 5,
217+
character: 30
218+
}
219+
});
220+
221+
expect(response).to.eql([
222+
{
223+
"uri": documentUri,
224+
"range": {
225+
"start": { "line": 0, "character": 0 },
226+
"end": { "line": 7, "character": 1 }
227+
}
228+
}
229+
]);
230+
});
231+
});

language-server/src/features/references.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export default {
6262
}
6363
}
6464
}
65+
6566
return schemaReferences;
6667
});
6768
},

language-server/src/features/references.test.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ describe("Feature - References", () => {
4444
test("don't return references that do not match location", async () => {
4545
const documentUri = await client.openDocument("./subject.schema.json", `{
4646
"$schema":"https://json-schema.org/draft/2020-12/schema",
47-
"$ref": "#/definitions/locations",
48-
"definitions":{
47+
"$ref": "#/$defs/locations",
48+
"$defs":{
4949
"names": {
5050
5151
},
@@ -67,7 +67,6 @@ describe("Feature - References", () => {
6767
expect(response).to.eql([]);
6868
});
6969

70-
7170
test("match one reference", async () => {
7271
const documentUri = await client.openDocument("./subject.schema.json", `{
7372
"$schema":"https://json-schema.org/draft/2020-12/schema",

0 commit comments

Comments
 (0)