-
Notifications
You must be signed in to change notification settings - Fork 726
/
document-analysis.ts
110 lines (99 loc) · 4.47 KB
/
document-analysis.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// ---------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// ---------------------------------------------------------------------------------------------
import { parseJsonPointer } from '../lib/ref/jsonpath';
import { JsonPath, SourceMap } from './source-map';
import { Location, Position } from 'vscode-languageserver';
import { value, stringify, parse, nodes, paths, } from "jsonpath";
/**
* @internal
* This class provides complex analysis for a document in relation to its peers, i.e.
* the other documents involved in one AutoRest/validation run (configuration file, OpenAPI documents).
* As such, it offers operations like:
* - looking up JSON paths/queries among all OpenAPI documents,
* - retrieving the data (and corresponding JSON path) that can be found at a certain text location.
*
* Essentially, it provides useful mappings between logical (JSON path) and physical (line/column) locations in both directions.
*/
export class DocumentAnalysis {
public constructor(
private documentUri: string,
private document: string,
private fullyResolvedAndMergedDefinition: any,
private fullyResolvedAndMergedDefinitionMap: SourceMap) { }
/**
* Determines the JSON query found at a given position.
* For example, suppressions will use JSON queries to limit the scope of the suppression (e.g. `$..parameters[?(@.name === 'quirkyParam')]`).
*
* @param position The position to look at for a JSON query.
*/
public GetJsonQueryAt(position: Position): string | null {
const lines = this.document.split("\n");
const potentialQuery: string = (lines[position.line].match(/\B\$[.[].+/g) || [])[0];
try {
parse(potentialQuery);
} catch (e) {
return null;
}
return potentialQuery;
}
/**
* Determines the JSON path equivalent to the JSON reference at a given position.
* OpenAPI definitions make use of these excessively.
*
* @param position The position to look at for a JSON reference.
*/
public GetJsonPathFromJsonReferenceAt(position: Position): string | null {
const fullyResolvedAndMergedDefinitionLocation = this.fullyResolvedAndMergedDefinitionMap.LookupForward(
this.documentUri,
position.line + 1, // VS Code deviates from the source map standard here... 0 vs. 1 based line numbers
position.character)[0];
if (fullyResolvedAndMergedDefinitionLocation) {
const path = fullyResolvedAndMergedDefinitionLocation.path;
if (path) {
// is $ref?
if (path.length > 0 && path[path.length - 1] === "$ref") {
// lookup object
const refValueJsonPointer: string = value(this.fullyResolvedAndMergedDefinition, stringify(path));
const refValueJsonPath: JsonPath = parseJsonPointer(refValueJsonPointer);
return stringify(refValueJsonPath);
}
} // else { console.warn("no object path for that location"); return null; }
} // else { console.log("reverse lookup failed (no big deal, may just hover empty space)"); return null; }
return null;
}
/**
* Retrieves all document locations (VS Code understands) corresponding with given JSON query.
*/
public * GetDocumentLocations(jsonQuery: string): Iterable<Location> {
for (const path of paths(this.fullyResolvedAndMergedDefinition, jsonQuery)) {
for (const mappingItem of this.fullyResolvedAndMergedDefinitionMap.LookupPath(path.slice(1))) {
yield {
uri: mappingItem.source,
range: {
start: {
line: mappingItem.originalLine - 1, // VS Code deviates from the source map standard here... 0 vs. 1 based line numbers
character: mappingItem.originalColumn
},
end: {
line: mappingItem.originalLine - 1,
character: mappingItem.originalColumn // TODO: room for improvement. think there even is extended information in `name`!
}
}
};
}
}
}
/**
* Retrieves all locations in the entire OpenAPI definition (and corresponding values) matching given JSON query.
*/
public * GetDefinitionLocations(jsonQuery: string): Iterable<{ value: any, jsonPath: string }> {
for (const path of nodes(this.fullyResolvedAndMergedDefinition, jsonQuery)) {
yield {
value: path.value,
jsonPath: stringify(path.path)
};
}
}
}