11import { workspace , ExtensionContext } from "vscode" ;
22import * as vscode from "vscode" ;
33
4+ import { writeFile } from "node:fs/promises" ;
5+ import { Readable } from "node:stream" ;
6+ import * as unzipper from "unzipper" ;
7+ import fs from "fs" ;
8+ import * as path from "path" ;
9+
410import {
511 LanguageClient ,
612 LanguageClientOptions ,
713 ServerOptions ,
8- TransportKind ,
914} from "vscode-languageclient/node" ;
1015
1116let client : LanguageClient ;
17+ let downloadLs = true ;
18+
19+ export function getArch ( ) : String | null {
20+ if ( process . arch == "x64" ) return "x86_64" ;
21+ if ( process . arch == "arm64" ) return "aarch64" ;
22+ else {
23+ vscode . window . showErrorMessage ( "Unsupported architecture: " + process . arch ) ;
24+ downloadLs = false ;
25+ return null ;
26+ }
27+ }
28+
29+ export function getPlatform ( ) : String | null {
30+ if ( process . platform == "darwin" ) return "apple-darwin" ;
31+ if ( process . platform == "win32" ) return "pc-windows-gnu" ;
32+ if ( process . platform == "linux" ) return "unknown-linux-gnu" ;
33+ else {
34+ vscode . window . showErrorMessage ( "Unsupported platform: " + process . platform ) ;
35+ downloadLs = false ;
36+ return null ;
37+ }
38+ }
39+
40+ async function downloadLSP ( context : ExtensionContext ) {
41+ const TAG = "v0.3.8" ;
42+ const URL = `https://github.com/errata-ai/vale-ls/releases/download/${ TAG } /vale-ls-${ getArch ( ) } -${ getPlatform ( ) } .zip` ;
43+ const extStorage = context . extensionPath ;
44+ const tmpZip = path . join ( extStorage , "vale-ls.zip" ) ;
1245
13- const TAG = "v0.3.8" ;
14- const URL =
15- "https://github.com/errata-ai/vale-ls/releases/download/{tag}/vale-ls-{arch}-{platform}.zip" ;
46+ vscode . window . showInformationMessage (
47+ "First launch: Downloading Vale Language Server"
48+ ) ;
49+
50+ const response = await fetch ( URL ) ;
51+ if ( response . body ) {
52+ const stream = Readable . fromWeb ( response . body ) ;
53+ await writeFile ( tmpZip , stream ) . then ( async ( ) => {
54+ // console.log("Downloaded to " + tmpZip);
55+ await unzipper . Open . file ( tmpZip ) . then ( ( directory ) => {
56+ // console.log("Extracting to " + extStorage);
57+ directory
58+ . extract ( { path : extStorage } )
59+ . then ( async ( ) => {
60+ fs . chmodSync ( path . join ( extStorage , "vale-ls" ) , 766 ) ;
61+ } )
62+ . finally ( ( ) => {
63+ fs . unlinkSync ( tmpZip ) ;
64+ vscode . window . showInformationMessage (
65+ "First launch: Vale Language Server downloaded"
66+ ) ;
67+ } ) ;
68+ } ) ;
69+ } ) ;
70+ } else {
71+ throw new Error ( "Failed to fetch the response body." ) ;
72+ }
73+ }
1674
1775type valeConfigOptions =
1876 | "configPath"
@@ -24,53 +82,49 @@ interface valeArgs {
2482 value : string ;
2583}
2684
27- export function getArch ( ) : String {
28- if ( process . arch == "x64" ) return "x86_64" ;
29- if ( process . arch == "arm64" ) return "aaarch64" ;
30- else return "unsupported" ;
31- }
32-
33- export function getPlatform ( ) : String {
34- if ( process . platform == "darwin" ) return "apple-darwin" ;
35- if ( process . platform == "win32" ) return "pc-windows-gnu" ;
36- else return "unknown-linux-gnu" ;
37- }
38-
39- export function activate ( context : ExtensionContext ) {
40- // Not possible when using `command`?
41- // let debugOptions = { execArgv: ['--nolazy', '--inspect=6009'] } ;
42- const valePath = context . extensionPath + "/tmp-bin/vale-ls" ;
43- //let valeArgs: any = new Object( );
44-
45- // TODO: Factor in https://vale.sh/docs/integrations/guide/#vale-ls
46- // Has the user defined a config file manually?
47- const configuration = vscode . workspace . getConfiguration ( ) ;
48- // Make global constant for now as will reuse and build upon later
49- let valeFilter : valeArgs = { value : "" } ;
50- let filters : string [ ] = [ ] ;
51- // let customConfigPath = configuration.get<string>("vale.valeCLI.config") ;
52- // if (customConfigPath) {
53- // console.log("Using config: " + customConfigPath);
54-
55- // valeArgs = {configPath: customConfigPath};
56- // }
57- // console.log(valeArgs)
58- console . log ( "Using binary: " + valePath ) ;
59-
60- // Handle old minAlertLevel to output as filter
61- if ( configuration . get ( "vale.valeCLI.minAlertLevel" ) !== "inherited" ) {
62- let minAlertLevel = configuration . get ( "vale.valeCLI.minAlertLevel" ) ;
63-
64- if ( minAlertLevel === "suggestion" ) {
65- filters . push ( `.Level in ["suggestion", "warning", "error"]` ) ;
66- }
67- if ( minAlertLevel === "warning" ) {
68- filters . push ( `.Level in ["warning", "error"]` ) ;
85+ export async function activate ( context : ExtensionContext ) {
86+ // TODO: Always needs reload on first activate
87+ const filePath = path . join ( context . extensionPath , "vale-ls" ) ;
88+ console . log ( filePath ) ;
89+ try {
90+ await vscode . workspace . fs . stat ( vscode . Uri . file ( filePath ) ) ;
91+ console . log ( "File exists" ) ;
92+ } catch {
93+ console . log ( "File doesn't exist" ) ;
94+
95+ await vscode . workspace . fs
96+ . createDirectory ( context . globalStorageUri )
97+ . then ( async ( ) => {
98+ await downloadLSP ( context ) ;
99+ } ) ;
100+ } finally {
101+ const valePath = path . join ( context . extensionPath , "vale-ls" ) ;
102+ // TODO: Must be a better way?
103+ var escapedPath = valePath . replace ( / ( \s ) / , "\\ " ) ;
104+
105+ // TODO: Factor in https://vale.sh/docs/integrations/guide/#vale-ls
106+ // Has the user defined a config file manually?
107+ const configuration = vscode . workspace . getConfiguration ( ) ;
108+ // Make global constant for now as will reuse and build upon later
109+ let valeFilter : valeArgs = { value : "" } ;
110+ let filters : string [ ] = [ ] ;
111+
112+ // console.log("Using binary: " + escapedPath);
113+
114+ // Handle old minAlertLevel to output as filter
115+ if ( configuration . get ( "vale.valeCLI.minAlertLevel" ) !== "inherited" ) {
116+ let minAlertLevel = configuration . get ( "vale.valeCLI.minAlertLevel" ) ;
117+
118+ if ( minAlertLevel === "suggestion" ) {
119+ filters . push ( `.Level in ["suggestion", "warning", "error"]` ) ;
120+ }
121+ if ( minAlertLevel === "warning" ) {
122+ filters . push ( `.Level in ["warning", "error"]` ) ;
123+ }
124+ if ( minAlertLevel === "error" ) {
125+ filters . push ( `.Level in ["error"]` ) ;
126+ }
69127 }
70- if ( minAlertLevel === "error" ) {
71- filters . push ( `.Level in ["error"]` ) ;
72- }
73- }
74128
75129 // Handle old enableSpellcheck to output as filter
76130 if ( configuration . get ( "vale.enableSpellcheck" ) === false ) {
@@ -82,40 +136,46 @@ export function activate(context: ExtensionContext) {
82136 valeFilter = filters . join ( " and " ) as unknown as valeArgs ;
83137 }
84138
85- let valeConfig : Record < valeConfigOptions , valeArgs > = {
86- configPath : configuration . get ( "vale.valeCLI.configPath" ) as valeArgs ,
87- syncOnStartup : configuration . get ( "vale.valeCLI.syncOnStartup" ) as valeArgs ,
88- filter : valeFilter as unknown as valeArgs ,
89- installVale : configuration . get ( "vale.valeCLI.installVale" ) as valeArgs ,
90- } ;
91- console . log ( valeConfig ) ;
92- // TODO: So do I need the below?
93- let tempArgs : never [ ] = [ ] ;
94- let serverOptions : ServerOptions = {
95- run : { command : valePath , args : tempArgs } ,
96- debug : { command : valePath , args : tempArgs } ,
97- } ;
98-
99- // Options to control the language client
100- let clientOptions : LanguageClientOptions = {
101- // TODO: Refine
102- initializationOptions : valeConfig ,
103- documentSelector : [ { scheme : "file" , language : "*" } ] ,
104- synchronize : {
105- fileEvents : workspace . createFileSystemWatcher ( "**/.clientrc" ) ,
106- } ,
107- } ;
108-
109- // Create the language client and start the client.
110- client = new LanguageClient (
111- "vale" ,
112- "Vale VSCode" ,
113- serverOptions ,
114- clientOptions
115- ) ;
116-
117- // Start the client. This will also launch the server
118- client . start ( ) ;
139+ let valeConfig : Record < valeConfigOptions , valeArgs > = {
140+ configPath : configuration . get ( "vale.valeCLI.configPath" ) as valeArgs ,
141+ syncOnStartup : configuration . get (
142+ "vale.valeCLI.syncOnStartup"
143+ ) as valeArgs ,
144+ filter : valeFilter as unknown as valeArgs ,
145+ installVale : configuration . get ( "vale.valeCLI.installVale" ) as valeArgs ,
146+ } ;
147+ // console.log(valeConfig);
148+ // TODO: So do I need the below?
149+ let tempArgs : never [ ] = [ ] ;
150+ let serverOptions : ServerOptions = {
151+ run : { command : escapedPath , args : tempArgs } ,
152+ debug : { command : escapedPath , args : tempArgs } ,
153+ } ;
154+
155+ // Options to control the language client
156+ let clientOptions : LanguageClientOptions = {
157+ // TODO: Refine
158+ initializationOptions : valeConfig ,
159+ documentSelector : [ { scheme : "file" , language : "*" } ] ,
160+ synchronize : {
161+ fileEvents : workspace . createFileSystemWatcher ( "**/.clientrc" ) ,
162+ } ,
163+ } ;
164+
165+ // Create the language client and start the client.
166+ client = new LanguageClient (
167+ "vale" ,
168+ "Vale VSCode" ,
169+ serverOptions ,
170+ clientOptions
171+ ) ;
172+
173+ // Start the client. This will also launch the server
174+ await client . start ( ) . catch ( ( err ) => {
175+ console . error ( err ) ;
176+ vscode . window . showErrorMessage ( "Failed to start Vale Language Server" ) ;
177+ } ) ;
178+ }
119179}
120180
121181export function deactivate ( ) : Thenable < void > | undefined {
0 commit comments