Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/extension-editing/src/packageDocumentL10nSupport.ts
13383 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import * as vscode from 'vscode';
7
import { getLocation, getNodeValue, parseTree, findNodeAtLocation, visit } from 'jsonc-parser';
8
9
10
const packageJsonSelector: vscode.DocumentSelector = { language: 'json', pattern: '**/package.json' };
11
const packageNlsJsonSelector: vscode.DocumentSelector = { language: 'json', pattern: '**/package.nls.json' };
12
13
export class PackageDocumentL10nSupport implements vscode.DefinitionProvider, vscode.ReferenceProvider, vscode.Disposable {
14
15
private readonly _disposables: vscode.Disposable[] = [];
16
17
constructor() {
18
this._disposables.push(vscode.languages.registerDefinitionProvider(packageJsonSelector, this));
19
this._disposables.push(vscode.languages.registerDefinitionProvider(packageNlsJsonSelector, this));
20
21
this._disposables.push(vscode.languages.registerReferenceProvider(packageNlsJsonSelector, this));
22
this._disposables.push(vscode.languages.registerReferenceProvider(packageJsonSelector, this));
23
}
24
25
dispose(): void {
26
for (const d of this._disposables) {
27
d.dispose();
28
}
29
}
30
31
public async provideDefinition(document: vscode.TextDocument, position: vscode.Position, _token: vscode.CancellationToken): Promise<vscode.DefinitionLink[] | undefined> {
32
const basename = document.uri.path.split('/').pop()?.toLowerCase();
33
if (basename === 'package.json') {
34
return this.provideNlsValueDefinition(document, position);
35
}
36
37
if (basename === 'package.nls.json') {
38
return this.provideNlsKeyDefinition(document, position);
39
}
40
41
return undefined;
42
}
43
44
private async provideNlsValueDefinition(packageJsonDoc: vscode.TextDocument, position: vscode.Position): Promise<vscode.DefinitionLink[] | undefined> {
45
const nlsRef = this.getNlsReferenceAtPosition(packageJsonDoc, position);
46
if (!nlsRef) {
47
return undefined;
48
}
49
50
const nlsUri = vscode.Uri.joinPath(packageJsonDoc.uri, '..', 'package.nls.json');
51
return this.resolveNlsDefinition(nlsRef, nlsUri);
52
}
53
54
private async provideNlsKeyDefinition(nlsDoc: vscode.TextDocument, position: vscode.Position): Promise<vscode.DefinitionLink[] | undefined> {
55
const nlsKey = this.getNlsKeyDefinitionAtPosition(nlsDoc, position);
56
if (!nlsKey) {
57
return undefined;
58
}
59
return this.resolveNlsDefinition(nlsKey, nlsDoc.uri);
60
}
61
62
private async resolveNlsDefinition(origin: { key: string; range: vscode.Range }, nlsUri: vscode.Uri): Promise<vscode.DefinitionLink[] | undefined> {
63
const target = await this.findNlsKeyDeclaration(origin.key, nlsUri);
64
if (!target) {
65
return undefined;
66
}
67
68
return [{
69
originSelectionRange: origin.range,
70
targetUri: target.uri,
71
targetRange: target.range,
72
}];
73
}
74
75
private getNlsReferenceAtPosition(packageJsonDoc: vscode.TextDocument, position: vscode.Position): { key: string; range: vscode.Range } | undefined {
76
const location = getLocation(packageJsonDoc.getText(), packageJsonDoc.offsetAt(position));
77
if (!location.previousNode || location.previousNode.type !== 'string') {
78
return undefined;
79
}
80
81
const value = getNodeValue(location.previousNode);
82
if (typeof value !== 'string') {
83
return undefined;
84
}
85
86
const match = value.match(/^%(.+)%$/);
87
if (!match) {
88
return undefined;
89
}
90
91
const nodeStart = packageJsonDoc.positionAt(location.previousNode.offset);
92
const nodeEnd = packageJsonDoc.positionAt(location.previousNode.offset + location.previousNode.length);
93
return { key: match[1], range: new vscode.Range(nodeStart, nodeEnd) };
94
}
95
96
public async provideReferences(document: vscode.TextDocument, position: vscode.Position, context: vscode.ReferenceContext, _token: vscode.CancellationToken): Promise<vscode.Location[] | undefined> {
97
const basename = document.uri.path.split('/').pop()?.toLowerCase();
98
if (basename === 'package.nls.json') {
99
return this.provideNlsKeyReferences(document, position, context);
100
}
101
if (basename === 'package.json') {
102
return this.provideNlsValueReferences(document, position, context);
103
}
104
return undefined;
105
}
106
107
private async provideNlsKeyReferences(nlsDoc: vscode.TextDocument, position: vscode.Position, context: vscode.ReferenceContext): Promise<vscode.Location[] | undefined> {
108
const nlsKey = this.getNlsKeyDefinitionAtPosition(nlsDoc, position);
109
if (!nlsKey) {
110
return undefined;
111
}
112
113
const packageJsonUri = vscode.Uri.joinPath(nlsDoc.uri, '..', 'package.json');
114
return this.findAllNlsReferences(nlsKey.key, packageJsonUri, nlsDoc.uri, context);
115
}
116
117
private async provideNlsValueReferences(packageJsonDoc: vscode.TextDocument, position: vscode.Position, context: vscode.ReferenceContext): Promise<vscode.Location[] | undefined> {
118
const nlsRef = this.getNlsReferenceAtPosition(packageJsonDoc, position);
119
if (!nlsRef) {
120
return undefined;
121
}
122
123
const nlsUri = vscode.Uri.joinPath(packageJsonDoc.uri, '..', 'package.nls.json');
124
return this.findAllNlsReferences(nlsRef.key, packageJsonDoc.uri, nlsUri, context);
125
}
126
127
private async findAllNlsReferences(nlsKey: string, packageJsonUri: vscode.Uri, nlsUri: vscode.Uri, context: vscode.ReferenceContext): Promise<vscode.Location[]> {
128
const locations = await this.findNlsReferencesInPackageJson(nlsKey, packageJsonUri);
129
130
if (context.includeDeclaration) {
131
const decl = await this.findNlsKeyDeclaration(nlsKey, nlsUri);
132
if (decl) {
133
locations.push(decl);
134
}
135
}
136
137
return locations;
138
}
139
140
private async findNlsKeyDeclaration(nlsKey: string, nlsUri: vscode.Uri): Promise<vscode.Location | undefined> {
141
try {
142
const nlsDoc = await vscode.workspace.openTextDocument(nlsUri);
143
const nlsTree = parseTree(nlsDoc.getText());
144
if (!nlsTree) {
145
return undefined;
146
}
147
148
const node = findNodeAtLocation(nlsTree, [nlsKey]);
149
if (!node?.parent) {
150
return undefined;
151
}
152
153
const keyNode = node.parent.children?.[0];
154
if (!keyNode) {
155
return undefined;
156
}
157
158
const start = nlsDoc.positionAt(keyNode.offset);
159
const end = nlsDoc.positionAt(keyNode.offset + keyNode.length);
160
return new vscode.Location(nlsUri, new vscode.Range(start, end));
161
} catch {
162
return undefined;
163
}
164
}
165
166
private async findNlsReferencesInPackageJson(nlsKey: string, packageJsonUri: vscode.Uri): Promise<vscode.Location[]> {
167
let packageJsonDoc: vscode.TextDocument;
168
try {
169
packageJsonDoc = await vscode.workspace.openTextDocument(packageJsonUri);
170
} catch {
171
return [];
172
}
173
174
const text = packageJsonDoc.getText();
175
const needle = `%${nlsKey}%`;
176
const locations: vscode.Location[] = [];
177
178
visit(text, {
179
onLiteralValue(value, offset, length) {
180
if (value === needle) {
181
const start = packageJsonDoc.positionAt(offset);
182
const end = packageJsonDoc.positionAt(offset + length);
183
locations.push(new vscode.Location(packageJsonUri, new vscode.Range(start, end)));
184
}
185
}
186
});
187
188
return locations;
189
}
190
191
private getNlsKeyDefinitionAtPosition(nlsDoc: vscode.TextDocument, position: vscode.Position): { key: string; range: vscode.Range } | undefined {
192
const location = getLocation(nlsDoc.getText(), nlsDoc.offsetAt(position));
193
194
// Must be on a top-level property key
195
if (location.path.length !== 1 || !location.isAtPropertyKey || !location.previousNode) {
196
return undefined;
197
}
198
199
const key = location.path[0] as string;
200
const start = nlsDoc.positionAt(location.previousNode.offset);
201
const end = nlsDoc.positionAt(location.previousNode.offset + location.previousNode.length);
202
return { key, range: new vscode.Range(start, end) };
203
}
204
}
205
206