4
4
* ------------------------------------------------------------------------------------------ */
5
5
6
6
import { mkdir , rm } from "node:fs/promises" ;
7
- import { ObjectEncodingOptions , existsSync } from "node:fs" ;
8
- import { exec , spawn , ExecOptions } from "node:child_process" ;
9
- import { workspace , window , ExtensionContext , commands , WorkspaceFolder , extensions } from "vscode" ;
7
+ import { existsSync } from "node:fs" ;
8
+ import { exec , spawn } from "node:child_process" ;
9
+ import * as vscode from "vscode" ;
10
10
import { get } from "node:https" ;
11
-
12
11
import { LanguageClient , LanguageClientOptions , ServerOptions } from "vscode-languageclient/node" ;
12
+ import { registerXmlFileAssociations , registerXPathSemanticTokensProvider } from "./xml" ;
13
+ import { execAsync as $ , guessRustTarget , makeStates } from "./utils" ;
13
14
14
15
let client : LanguageClient ;
15
16
16
- function guessRustTarget ( ) {
17
- const platform = process . platform ;
18
- const arch = process . arch ;
19
- if ( platform === "win32" ) {
20
- if ( arch === "x64" ) return "x86_64-pc-windows-msvc" ;
21
- return "i686-pc-windows-msvc" ;
22
- }
23
- if ( platform === "darwin" ) {
24
- if ( arch === "x64" ) return "x86_64-apple-darwin" ;
25
- if ( arch === "arm64" ) return "aarch64-apple-darwin" ;
26
- } else if ( platform === "linux" ) {
27
- if ( arch === "x64" ) return "x86_64-unknown-linux-gnu" ;
28
- return "i686-unknown-linux-gnu" ;
29
- }
30
- }
31
-
32
- function execAsync ( command : string , options ?: ObjectEncodingOptions & ExecOptions ) {
33
- return new Promise < { stdout : string | Buffer ; stderr : string | Buffer } > ( ( resolve , reject ) => {
34
- exec ( command , options , ( err , stdout , stderr ) => {
35
- if ( err ) reject ( err ) ;
36
- else resolve ( { stdout, stderr } ) ;
37
- } ) ;
38
- } ) ;
39
- }
40
-
41
17
const repo = "https://github.com/Desdaemon/odoo-lsp" ;
42
18
43
- async function downloadLspBinary ( context : ExtensionContext ) {
19
+ async function downloadLspBinary ( context : vscode . ExtensionContext ) {
44
20
const isWindows = process . platform === "win32" ;
45
21
const archiveExtension = isWindows ? ".zip" : ".tgz" ;
46
22
const runtimeDir = context . globalStorageUri . fsPath ;
47
23
await mkdir ( runtimeDir , { recursive : true } ) ;
48
- const preferNightly = ! ! workspace . getConfiguration ( "odoo-lsp.binary" ) . get ( "preferNightly" ) ;
49
- const overrideVersion : string | undefined = workspace . getConfiguration ( "odoo-lsp.binary" ) . get ( "overrideVersion" ) ;
24
+ const preferNightly = ! ! vscode . workspace . getConfiguration ( "odoo-lsp.binary" ) . get ( "preferNightly" ) ;
25
+ const overrideVersion : string | undefined = vscode . workspace
26
+ . getConfiguration ( "odoo-lsp.binary" )
27
+ . get ( "overrideVersion" ) ;
50
28
51
29
let release = overrideVersion ;
52
30
if ( ! preferNightly && ! overrideVersion ) {
@@ -70,7 +48,7 @@ async function downloadLspBinary(context: ExtensionContext) {
70
48
const latest = releases . find ( ( r ) => r . name === "nightly" ) ;
71
49
resolve ( latest ?. tag_name ) ;
72
50
} catch ( err ) {
73
- window . showErrorMessage ( `Unable to fetch nightly release: ${ err } ` ) ;
51
+ vscode . window . showErrorMessage ( `Unable to fetch nightly release: ${ err } ` ) ;
74
52
resolve ( context . extension . packageJSON . _release ) ;
75
53
}
76
54
} ) ;
@@ -79,7 +57,7 @@ async function downloadLspBinary(context: ExtensionContext) {
79
57
) ) || release ;
80
58
}
81
59
if ( typeof release !== "string" || ! release ) {
82
- window . showErrorMessage ( `Bug: invalid release "${ release } "` ) ;
60
+ vscode . window . showErrorMessage ( `Bug: invalid release "${ release } "` ) ;
83
61
return ;
84
62
}
85
63
const latest = `${ runtimeDir } /${ release } ${ archiveExtension } ` ;
@@ -101,7 +79,7 @@ async function downloadLspBinary(context: ExtensionContext) {
101
79
if ( await which ( "cargo-binstall" ) ) {
102
80
actions . push ( Actions . Binstall ) ;
103
81
}
104
- const resp = await window . showInformationMessage (
82
+ const resp = await vscode . window . showInformationMessage (
105
83
`odoo-lsp: No prebuilt binaries available for your platform (platform=${ process . platform } , arch=${ process . arch } ).
106
84
Please file an issue at ${ repo } , or install odoo-lsp from source.` ,
107
85
...actions ,
@@ -111,7 +89,7 @@ async function downloadLspBinary(context: ExtensionContext) {
111
89
await openLink ( repo ) ;
112
90
break ;
113
91
case Actions . InstallSource : {
114
- const channel = window . createOutputChannel ( "cargo install odoo-lsp" ) ;
92
+ const channel = vscode . window . createOutputChannel ( "cargo install odoo-lsp" ) ;
115
93
channel . show ( ) ;
116
94
context . subscriptions . push ( channel ) ;
117
95
const cargo = spawn ( `cargo install --git ${ repo } ` , { shell : true } ) ;
@@ -127,7 +105,7 @@ async function downloadLspBinary(context: ExtensionContext) {
127
105
break ;
128
106
}
129
107
case Actions . Binstall : {
130
- const channel = window . createOutputChannel ( "cargo-binstall odoo-lsp" ) ;
108
+ const channel = vscode . window . createOutputChannel ( "cargo-binstall odoo-lsp" ) ;
131
109
channel . show ( ) ;
132
110
context . subscriptions . push ( channel ) ;
133
111
const cargo = spawn ( "cargo binstall odoo-lsp" , { shell : true } ) ;
@@ -156,38 +134,38 @@ async function downloadLspBinary(context: ExtensionContext) {
156
134
const powershell = { shell : "powershell.exe" } ;
157
135
const sh = { shell : "sh" } ;
158
136
if ( ! existsSync ( latest ) ) {
159
- window . setStatusBarMessage ( `Downloading odoo-lsp@${ release } ...` , 5 ) ;
137
+ vscode . window . setStatusBarMessage ( `Downloading odoo-lsp@${ release } ...` , 5 ) ;
160
138
try {
161
139
if ( isWindows ) {
162
- await execAsync ( `Invoke-WebRequest -Uri ${ link } -OutFile ${ latest } ` , powershell ) ;
163
- await execAsync ( `Invoke-WebRequest -Uri ${ shaLink } -OutFile ${ shaOutput } ` , powershell ) ;
164
- const { stdout } = await execAsync (
140
+ await $ ( `Invoke-WebRequest -Uri ${ link } -OutFile ${ latest } ` , powershell ) ;
141
+ await $ ( `Invoke-WebRequest -Uri ${ shaLink } -OutFile ${ shaOutput } ` , powershell ) ;
142
+ const { stdout } = await $ (
165
143
`(Get-FileHash ${ latest } -Algorithm SHA256).Hash -eq (Get-Content ${ shaOutput } )` ,
166
144
powershell ,
167
145
) ;
168
146
if ( stdout . toString ( ) . trim ( ) !== "True" ) throw new Error ( "Checksum verification failed" ) ;
169
- await execAsync ( `Expand-Archive -Path ${ latest } -DestinationPath ${ runtimeDir } ` , powershell ) ;
147
+ await $ ( `Expand-Archive -Path ${ latest } -DestinationPath ${ runtimeDir } ` , powershell ) ;
170
148
} else {
171
- await execAsync ( `wget -O ${ latest } ${ link } ` , sh ) ;
172
- await execAsync ( `wget -O ${ shaOutput } ${ shaLink } ` , sh ) ;
173
- await execAsync (
149
+ await $ ( `wget -O ${ latest } ${ link } ` , sh ) ;
150
+ await $ ( `wget -O ${ shaOutput } ${ shaLink } ` , sh ) ;
151
+ await $ (
174
152
`if [ "$(shasum -a 256 ${ latest } | cut -d ' ' -f 1)" != "$(cat ${ shaOutput } )" ]; then exit 1; fi` ,
175
153
sh ,
176
154
) ;
177
- await execAsync ( `tar -xzf ${ latest } -C ${ runtimeDir } ` , sh ) ;
155
+ await $ ( `tar -xzf ${ latest } -C ${ runtimeDir } ` , sh ) ;
178
156
}
179
157
} catch ( err ) {
180
158
// We only build nightly when there are changes, so there will be days without nightly builds.
181
159
if ( ! ( err instanceof Error ) || ! err . message . includes ( "404" ) ) {
182
- window . showErrorMessage ( `Failed to download odoo-lsp binary: ${ err } ` ) ;
160
+ vscode . window . showErrorMessage ( `Failed to download odoo-lsp binary: ${ err } ` ) ;
183
161
}
184
162
await rm ( latest ) ;
185
163
}
186
164
} else if ( ! existsSync ( odooLspBin ) ) {
187
165
if ( isWindows ) {
188
- await execAsync ( `Expand-Archive -Path ${ latest } -DestinationPath ${ runtimeDir } ` , powershell ) ;
166
+ await $ ( `Expand-Archive -Path ${ latest } -DestinationPath ${ runtimeDir } ` , powershell ) ;
189
167
} else {
190
- await execAsync ( `tar -xzf ${ latest } -C ${ runtimeDir } ` , sh ) ;
168
+ await $ ( `tar -xzf ${ latest } -C ${ runtimeDir } ` , sh ) ;
191
169
}
192
170
}
193
171
@@ -213,63 +191,30 @@ async function openLink(url: string) {
213
191
} else {
214
192
opener = "xdg-open" ;
215
193
}
216
- return await execAsync ( `${ opener } ${ url } ` ) ;
194
+ return await $ ( `${ opener } ${ url } ` ) ;
217
195
}
218
196
219
- export async function activate ( context : ExtensionContext ) {
220
- const traceOutputChannel = window . createOutputChannel ( "Odoo LSP Extension" ) ;
197
+ const makeExtensionState = ( context : vscode . ExtensionContext ) => makeStates ( context , {
198
+ noXmlReminders : Boolean ,
199
+ noXPathReminders : Boolean ,
200
+ } ) ;
221
201
222
- try {
223
- // see https://github.com/redhat-developer/vscode-xml/blob/main/src/api/xmlExtensionApi.ts
224
- const xmlApi = await extensions . getExtension < XMLExtensionApi > ( "redhat.vscode-xml" ) ?. activate ( ) ;
225
- if ( xmlApi ) {
226
- xmlApi . addXMLFileAssociations ( [
227
- // HACK: systemId must be unique
228
- {
229
- systemId : `${ context . extensionUri } /odoo.rng` ,
230
- pattern : "views/*.xml" ,
231
- } ,
232
- {
233
- systemId : `${ context . extensionUri } /./odoo.rng` ,
234
- pattern : "data/*.xml" ,
235
- } ,
236
- {
237
- systemId : `${ context . extensionUri } /././odoo.rng` ,
238
- pattern : "security/*.xml" ,
239
- } ,
240
- {
241
- systemId : `${ context . extensionUri } /./././odoo.rng` ,
242
- pattern : "report/*.xml" ,
243
- } ,
244
- {
245
- systemId : `${ context . extensionUri } /././././odoo.rng` ,
246
- pattern : "wizard/*.xml" ,
247
- } ,
248
- ] ) ;
249
- } else {
250
- // Recommend that the user install the XML extension
251
- window
252
- . showInformationMessage (
253
- "Install the 'XML' extension for a better XML editing experience." ,
254
- "Install" ,
255
- "Remind me later" ,
256
- )
257
- . then ( ( choice ) => {
258
- if ( choice !== "Install" ) return ;
259
- commands . executeCommand ( "workbench.extensions.search" , "@id:redhat.vscode-xml" ) ;
260
- } ) ;
261
- }
262
- } catch ( err ) {
263
- traceOutputChannel . appendLine ( `Failed to register XML file associations: ${ err } ` ) ;
264
- }
202
+ export type State = ReturnType < typeof makeExtensionState > ;
203
+
204
+ export async function activate ( context : vscode . ExtensionContext ) {
205
+ const traceOutputChannel = vscode . window . createOutputChannel ( "Odoo LSP Extension" ) ;
206
+ const extensionState = makeExtensionState ( context ) ;
207
+
208
+ await registerXmlFileAssociations ( context , traceOutputChannel , extensionState ) ;
209
+ await registerXPathSemanticTokensProvider ( context , traceOutputChannel , extensionState ) ;
265
210
266
211
let command = process . env . SERVER_PATH || "odoo-lsp" ;
267
212
if ( ! ( await which ( command ) ) ) {
268
213
command = ( await downloadLspBinary ( context ) ) || command ;
269
214
}
270
215
traceOutputChannel . appendLine ( `odoo-lsp executable: ${ command } ` ) ;
271
216
if ( ! ( await which ( command ) ) ) {
272
- window . showErrorMessage ( `no odoo-lsp executable present: ${ command } ` ) ;
217
+ vscode . window . showErrorMessage ( `no odoo-lsp executable present: ${ command } ` ) ;
273
218
return ;
274
219
}
275
220
const serverOptions : ServerOptions = {
@@ -293,24 +238,24 @@ export async function activate(context: ExtensionContext) {
293
238
{ language : "javascript" , scheme : "file" } ,
294
239
] ,
295
240
synchronize : {
296
- fileEvents : workspace . createFileSystemWatcher ( "**/.odoo_lsp*" ) ,
241
+ fileEvents : vscode . workspace . createFileSystemWatcher ( "**/.odoo_lsp*" ) ,
297
242
} ,
298
243
traceOutputChannel,
299
244
} ;
300
245
301
246
context . subscriptions . push (
302
- commands . registerCommand ( "odoo-lsp.tsconfig" , async ( ) => {
303
- const activeWindow = window . activeTextEditor ?. document . uri . fsPath ;
304
- let folder : WorkspaceFolder | undefined ;
247
+ vscode . commands . registerCommand ( "odoo-lsp.tsconfig" , async ( ) => {
248
+ const activeWindow = vscode . window . activeTextEditor ?. document . uri . fsPath ;
249
+ let folder : vscode . WorkspaceFolder | undefined ;
305
250
if ( activeWindow ) {
306
- folder = workspace . workspaceFolders ?. find ( ( ws ) => activeWindow . includes ( ws . uri . fsPath ) ) ;
251
+ folder = vscode . workspace . workspaceFolders ?. find ( ( ws ) => activeWindow . includes ( ws . uri . fsPath ) ) ;
307
252
}
308
253
309
- if ( ! folder ) folder = await window . showWorkspaceFolderPick ( ) ;
254
+ if ( ! folder ) folder = await vscode . window . showWorkspaceFolderPick ( ) ;
310
255
if ( ! folder ) return ;
311
256
312
257
const selection =
313
- ( await window . showOpenDialog ( {
258
+ ( await vscode . window . showOpenDialog ( {
314
259
canSelectFiles : false ,
315
260
canSelectFolders : true ,
316
261
canSelectMany : true ,
@@ -319,31 +264,31 @@ export async function activate(context: ExtensionContext) {
319
264
} ) ) ?? [ ] ;
320
265
321
266
const paths = selection . map ( ( sel ) => `--addons-path ${ sel . fsPath } ` ) . join ( " " ) ;
322
- const { stdout } = await execAsync ( `${ command } tsconfig ${ paths } ` , {
267
+ const { stdout } = await $ ( `${ command } tsconfig ${ paths } ` , {
323
268
cwd : folder . uri . fsPath ,
324
269
} ) ;
325
270
326
- const doc = await workspace . openTextDocument ( {
271
+ const doc = await vscode . workspace . openTextDocument ( {
327
272
language : "json" ,
328
273
content : stdout as string ,
329
274
} ) ;
330
- await window . showTextDocument ( doc ) ;
275
+ await vscode . window . showTextDocument ( doc ) ;
331
276
} ) ,
332
277
) ;
333
278
334
279
context . subscriptions . push (
335
- commands . registerCommand ( "odoo-lsp.statistics" , async ( ) => {
280
+ vscode . commands . registerCommand ( "odoo-lsp.statistics" , async ( ) => {
336
281
const response = await client . sendRequest ( "odoo-lsp/statistics" ) ;
337
- const doc = await workspace . openTextDocument ( {
282
+ const doc = await vscode . workspace . openTextDocument ( {
338
283
language : "json" ,
339
284
content : JSON . stringify ( response , undefined , 2 ) ,
340
285
} ) ;
341
- await window . showTextDocument ( doc ) ;
286
+ await vscode . window . showTextDocument ( doc ) ;
342
287
} ) ,
343
288
) ;
344
289
345
290
context . subscriptions . push (
346
- commands . registerCommand ( "odoo-lsp.restart-lsp" , async ( ) => {
291
+ vscode . commands . registerCommand ( "odoo-lsp.restart-lsp" , async ( ) => {
347
292
await client . restart ( ) ;
348
293
traceOutputChannel . appendLine ( "Odoo LSP restarted" ) ;
349
294
} ) ,
@@ -360,8 +305,3 @@ export function deactivate(): Thenable<void> | undefined {
360
305
}
361
306
return client . stop ( ) ;
362
307
}
363
-
364
- interface XMLExtensionApi {
365
- // addXMLCatalogs(_: string[]): void;
366
- addXMLFileAssociations ( _ : { systemId : string ; pattern : string } [ ] ) : void ;
367
- }
0 commit comments