diff --git a/client/liquid-java-0.0.15.vsix b/client/liquid-java-0.0.15.vsix index 20c262b..d562948 100644 --- a/client/liquid-java-0.0.15.vsix +++ b/client/liquid-java-0.0.15.vsix @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c99792c43e9fdc8d6223e26464b86bbac339f7f34830ad2510b5c26cfd6e95b5 -size 72270316 +oid sha256:6723b5c655aa561448e6ef21599ea62d2c5196855c93718ec9a277b6773ee274 +size 72271657 diff --git a/client/server/language-server-liquidjava.jar b/client/server/language-server-liquidjava.jar deleted file mode 100644 index 513237d..0000000 Binary files a/client/server/language-server-liquidjava.jar and /dev/null differ diff --git a/client/src/constants.ts b/client/src/constants.ts new file mode 100644 index 0000000..8fa8faf --- /dev/null +++ b/client/src/constants.ts @@ -0,0 +1,19 @@ +export const API_JAR_GLOB = "lib/liquidjava-api*.jar"; +export const SERVER_JAR_FILENAME = "vscode-liquidjava.jar"; +export const DEBUG_MODE = false; +export const DEBUG_PORT = 50000; +export const LIQUIDJAVA_SCOPES = [ + "source.liquidjava keyword.other.liquidjava", + "source.liquidjava entity.name.function.liquidjava", + "source.liquidjava storage.type.primitive.liquidjava", + "source.liquidjava entity.name.type.liquidjava", + "source.liquidjava entity.name.type.class.liquidjava", + "source.liquidjava entity.name.type.externalref.liquidjava", + "source.liquidjava variable.other.liquidjava", + "source.liquidjava keyword.operator.liquidjava", + "source.liquidjava constant.language.boolean.liquidjava", + "source.liquidjava constant.numeric.liquidjava", + "keyword.operator.liquidjava", + "constant.language.boolean.liquidjava", + "constant.numeric.liquidjava", +]; \ No newline at end of file diff --git a/client/src/decorators.ts b/client/src/decorators.ts index c027a64..ddc41a9 100644 --- a/client/src/decorators.ts +++ b/client/src/decorators.ts @@ -1,26 +1,11 @@ import * as vscode from "vscode"; +import { LIQUIDJAVA_SCOPES } from "./constants"; type TMRule = { scope: string | string[]; settings: { fontStyle?: string; foreground?: string } }; -const LIQUIDJAVA_SCOPES = [ - "source.liquidjava keyword.other.liquidjava", - "source.liquidjava entity.name.function.liquidjava", - "source.liquidjava storage.type.primitive.liquidjava", - "source.liquidjava entity.name.type.liquidjava", - "source.liquidjava entity.name.type.class.liquidjava", - "source.liquidjava entity.name.type.externalref.liquidjava", - "source.liquidjava variable.other.liquidjava", - "source.liquidjava keyword.operator.liquidjava", - "source.liquidjava constant.language.boolean.liquidjava", - "source.liquidjava constant.numeric.liquidjava", - "keyword.operator.liquidjava", - "constant.language.boolean.liquidjava", - "constant.numeric.liquidjava", -]; - /** * Apply italic fontStyle to LiquidJava TextMate scopes * This preserves the active theme's colors and only adds italic style to injected tokens diff --git a/client/src/extension.ts b/client/src/extension.ts index e818fde..1b85f52 100644 --- a/client/src/extension.ts +++ b/client/src/extension.ts @@ -1,17 +1,12 @@ import * as vscode from "vscode"; -import * as fs from "fs"; import * as path from "path"; import * as net from "net"; import * as child_process from "child_process"; -import { Trace } from "vscode-jsonrpc"; import { LanguageClient, LanguageClientOptions, StreamInfo, ServerOptions, State } from "vscode-languageclient"; import { LiquidJavaLogger, createLogger } from "./logging"; import { applyItalicOverlay } from "./decorators"; - -const SERVER_JAR_FILENAME = "language-server-liquidjava.jar"; -const API_JAR_GLOB= "lib/liquidjava-api*.jar"; -const DEBUG = false; -const DEBUG_PORT = 50000; +import { connectToPort, findJavaExecutable, getAvailablePort, isJarPresent, killProcess } from "./utils"; +import { SERVER_JAR_FILENAME, DEBUG_MODE, DEBUG_PORT } from "./constants"; let serverProcess: child_process.ChildProcess; let client: LanguageClient; @@ -68,15 +63,6 @@ export async function deactivate() { await stopExtension("Extension was deactivated"); } -/** - * Checks if the extension can be activated by looking for the LiquidJava API jar in the workspace - * @returns true if the extension can be activated, false otherwise - */ -async function isJarPresent(): Promise { - const uris = await vscode.workspace.findFiles(API_JAR_GLOB); - return uris.length > 0; -} - /** * Initializes logging for the extension with an output channel * @param context The extension context @@ -125,8 +111,8 @@ function updateStatusBar(state: "loading" | "stopped" | "passed" | "failed") { * @returns A promise to the port number the server is running on */ async function runLanguageServer(context: vscode.ExtensionContext, javaExecutablePath: string): Promise { - const port = DEBUG ? DEBUG_PORT : await getAvailablePort(); - if (DEBUG) { + const port = DEBUG_MODE ? DEBUG_PORT : await getAvailablePort(); + if (DEBUG_MODE) { logger.client.info("DEBUG MODE: Using fixed port " + port); return port; } @@ -187,8 +173,6 @@ async function runClient(context: vscode.ExtensionContext, port: number) { documentSelector: [{ language: "java" }], }; client = new LanguageClient("liquidJavaServer", "LiquidJava Server", serverOptions, clientOptions); - client.trace = Trace.Verbose; // for debugging - client.onDidChangeState((e) => { if (e.newState === State.Stopped) { stopExtension("Extension stopped"); @@ -221,93 +205,6 @@ async function runClient(context: vscode.ExtensionContext, port: number) { ); } -/** - * Finds the Java executable in the system, either in JAVA_HOME or in PATH - * MIT Licensed code from: https://github.com/georgewfraser/vscode-javac - */ -function findJavaExecutable(binname: string): string | null { - binname = process.platform === "win32" ? `${binname}.exe` : binname; - - // First search each JAVA_HOME bin folder - if (process.env["JAVA_HOME"]) { - for (const workspace of process.env["JAVA_HOME"].split(path.delimiter)) { - const binpath = path.join(workspace, "bin", binname); - if (fs.existsSync(binpath)) return binpath; - } - } - // Then search PATH parts - if (process.env["PATH"]) { - for (const part of process.env["PATH"].split(path.delimiter)) { - const binpath = path.join(part, binname); - if (fs.existsSync(binpath)) return binpath; - } - } - // Else return null - return null; -} - -/** - * Gets an available port in the OS - * @returns A promise to the available port number - */ -async function getAvailablePort(): Promise { - return new Promise((resolve, reject) => { - const server = net.createServer(); - server.listen(0, "localhost", () => { - const port = (server.address() as net.AddressInfo).port; - server.close(); - resolve(port); - }); - server.on("error", reject); - }); -} - -/** - * Connects to the process on the given port, retrying until timeout - * @param port The port to connect to - * @param timeout The timeout duration in milliseconds - * @param attemptInterval The interval between connection attempts in milliseconds - * @param connectionTimeout The timeout for each individual connection attempt in milliseconds - * @returns A promise to the connected socket - */ -async function connectToPort( - port: number, - timeout = 10000, - attemptInterval = 500, - connectionTimeout = 500 -): Promise { - const deadline = Date.now() + timeout; - while (Date.now() < deadline) { - try { - return await new Promise((resolve, reject) => { - const s = net.connect({ port }); - - // connection timeout - const t = setTimeout(() => { - s.destroy(new Error("connection timeout")); - reject(new Error("connection timeout")); - }, connectionTimeout); - - // success - s.once("connect", () => { - clearTimeout(t); - resolve(s); - }); - - // failure - s.once("error", (e) => { - clearTimeout(t); - reject(e); - }); - }); - } catch { - // wait and retry - await new Promise((r) => setTimeout(r, attemptInterval)); - } - } - throw new Error(`Server not reachable on port ${port} within ${timeout}ms`); -} - /** * Stops the LiquidJava extension * @param reason The reason for stopping the extension @@ -342,36 +239,3 @@ async function stopExtension(reason: string) { await killProcess(serverProcess); serverProcess = undefined; } - -/** - * Kills the given process if it is running - * @param proc The process to kill - * @returns A promise that resolves when the process has been killed - */ -async function killProcess(proc?: child_process.ChildProcess) { - return new Promise((resolve, reject) => { - if (!proc || proc.killed) { - // already killed - resolve(); - return; - } - if (process.platform === "win32") { - // Windows - child_process.exec(`taskkill /pid ${proc.pid} /T /F`, (err) => { - if (err) { - reject(err); - } else { - resolve(); - } - }); - } else { - // Unix - try { - process.kill(proc.pid, "SIGKILL"); - resolve(); - } catch (err) { - reject(err); - } - } - }); -} diff --git a/client/src/extension_debug.ts b/client/src/extension_debug.ts deleted file mode 100644 index ecaa296..0000000 --- a/client/src/extension_debug.ts +++ /dev/null @@ -1,111 +0,0 @@ -'use strict'; - -import * as net from 'net'; - -import {Trace} from 'vscode-jsonrpc'; -import { window, workspace, commands, ExtensionContext, Uri } from 'vscode'; -import { LanguageClient, LanguageClientOptions, StreamInfo, Position as LSPosition, Location as LSLocation } from 'vscode-languageclient'; -import * as vscode from 'vscode'; -import * as fs from 'fs'; - -let diagnosticCollection: vscode.DiagnosticCollection; - -export function activate(context: ExtensionContext) { - //Checking if liquid java api is inside workspace, - //if yes connect to server, otherwise return - - //https://github.com/ev3dev/vscode-ev3dev-browser/wiki/Glob-Patterns - const glob = '**/liquidjava-api*.jar';//or +'/{*.png,*.jpeg}'; - vscode.workspace.findFiles(glob, null, 100).then((uris: vscode.Uri[] ) => { - if(uris.length == 0){ - console.log("No references to liquidJava api in workspace"); - vscode.window.showInformationMessage("Not using LiquidJava - Api not in the workspace"); - return; - }else{ - vscode.window.showInformationMessage("Found LiquidJava Api in the workspace - Loading Extension"); - } - uris.forEach((uri: vscode.Uri) => { - console.log("Found uri:"+uri); - }); - - - - // The server is a started as a separate app and listens on port 50000 - const connectionInfo = { - port: 50000 - }; - const serverOptions = () => { - // Connect to language server via socket - const socket = net.connect(connectionInfo); - const result: StreamInfo = { - writer: socket, - reader: socket - }; - return Promise.resolve(result); - }; - - const clientOptions: LanguageClientOptions = { - documentSelector: ['java'], - synchronize: { - fileEvents: workspace.createFileSystemWatcher('**/*.java') - } - }; - - // Create the language client and start the client. - const lc = new LanguageClient('LiquidJava Server', serverOptions, clientOptions); - lc.trace = Trace.Messages; - const disposable = lc.start(); - }); - -// var disposable2 =commands.registerCommand("adl.a.proxy", async () => { -// let activeEditor = window.activeTextEditor; -// if (!activeEditor || !activeEditor.document || activeEditor.document.languageId !== 'plaintext') { -// return; -// } - -// if (activeEditor.document.uri instanceof Uri) { -// commands.executeCommand("adl.a", activeEditor.document.uri.toString()); -// } -// }) - -// context.subscriptions.push(disposable2); - -// enable tracing (.Off, .Messages, Verbose) - - -// Push the disposable to the context's subscriptions so that the -// client can be deactivated on extension deactivation -// context.subscriptions.push(disposable); - - -// diagnosticCollection = vscode.languages.createDiagnosticCollection('go'); -// context.subscriptions.push(diagnosticCollection); - -} - -// export function deactivate(): Thenable | undefined { -// if (!client) { -// return undefined; -// } -// return client.stop(); -// } - -// function onChange() { -// diagnosticCollection.clear(); -// // let uri = document.uri; -// // check(uri.fsPath, goConfig).then(errors => { -// // diagnosticCollection.clear(); -// // let diagnosticMap: Map = new Map(); -// // errors.forEach(error => { -// // let canonicalFile = vscode.Uri.file(error.file).toString(); -// // let range = new vscode.Range(error.line-1, error.startColumn, error.line-1, error.endColumn); -// // let diagnostics = diagnosticMap.get(canonicalFile); -// // if (!diagnostics) { diagnostics = []; } -// // diagnostics.push(new vscode.Diagnostic(range, error.msg, error.severity)); -// // diagnosticMap.set(canonicalFile, diagnostics); -// // }); -// // diagnosticMap.forEach((diags, file) => { -// // diagnosticCollection.set(vscode.Uri.parse(file), diags); -// // }); -// // }) -// } \ No newline at end of file diff --git a/client/src/extension_not_network.ts b/client/src/extension_not_network.ts deleted file mode 100644 index 43b7148..0000000 --- a/client/src/extension_not_network.ts +++ /dev/null @@ -1,386 +0,0 @@ -import * as vscode from 'vscode'; -import * as fs from "fs"; -import * as path from 'path'; -import {Trace} from 'vscode-jsonrpc'; -import * as net from 'net'; -import * as child_process from "child_process"; - -import { workspace, Disposable, ExtensionContext } from 'vscode'; -import { LanguageClient, LanguageClientOptions, ServerOptions, SettingMonitor, StreamInfo, TransportKind } from 'vscode-languageclient'; - -import { LiquidJavaProvider } from './liquidJavaProvider'; -import { Executable } from 'vscode-languageclient/lib/client'; - -export function activate(context: vscode.ExtensionContext) { - const activeTextEditor = vscode.window.activeTextEditor; - const glob = '**/liquidjava-api*.jar';//or +'/{*.png,*.jpeg}'; - vscode.workspace.findFiles(glob, null, 100).then((uris: vscode.Uri[] ) => { - if(uris.length == 0){ - console.log("No references to liquidJava api in workspace"); - vscode.window.showInformationMessage("Not using LiquidJava - Api not in the workspace"); - return; - }else{ - vscode.window.showInformationMessage("Found LiquidJava Api in the workspace - Loading Extension"); - } - uris.forEach((uri: vscode.Uri) => { - console.log("Found uri:"+uri); - }); - - //Start server only if api is inside the project - - //#1 - const { JAVA_HOME } = process.env; - const main = 'App'; - console.log(`Using java from JAVA_HOME: ${JAVA_HOME}`); - if (JAVA_HOME) { - // Java execution path. - const executable: string = path.join(JAVA_HOME,'bin', 'java'); - - // C:\Program Files\Java\jdk1.8.0_231\bin\bin\java - - // path to the launcher.jar - const classPath = path.join(__dirname, 'server', 'language-server-liquidjava.jar'); - const args: string[] = ['-cp', classPath]; - - // Set the server options - // -- java execution path - // -- argument to be pass when executing the java command - const serverOptions: ServerOptions = { - command: executable, - args: [...args, main], - options: {} - }; - - // Options to control the language client - const clientOptions: LanguageClientOptions = { - documentSelector: ['java']//, - // synchronize: { - // fileEvents: workspace.createFileSystemWatcher('**/*.java') - // } - }; - - // Create the language client and start the client. - const disposable = new LanguageClient('LiquidJava Server','LiquidJava Server', serverOptions, clientOptions).start(); - - // Disposables to remove on deactivation. - context.subscriptions.push(disposable); - } - - - //#2 - // let javaExecutablePath = findJavaExecutable('java'); - // //// let serverCommand = javaExecutablePath + " -jar "; - // let extensionDir = __dirname;//context.extensionPath; - // let classPath = path.join(extensionDir, '..', 'server', 'language-server-liquidjava.jar'); - // const args: string[] = ['-cp', classPath]; - // const main: string = 'Main'; - - // let debugOptions = { execArgv: ["--nolazy", "--debug=6009"] }; - - // let serverOptions:ServerOptions = { - // command: javaExecutablePath, - // args: [...args, main], - // transport: TransportKind.ipc - // }; - // console.log(serverOptions); - - - - // let clientOptions: LanguageClientOptions = { - // documentSelector: ['java'], - // synchronize: { - // fileEvents: workspace.createFileSystemWatcher('**/*.java') - // } - // }; - - // // Create the language client and start the client. - // let lc = new LanguageClient('LiquidJava Server', serverOptions, clientOptions); - - // lc.trace = Trace.Verbose; - - // let disposable = lc.start(); - - // // Push the disposable to the context's subscriptions so that the - // // client can be deactivated on extension deactivation - // context.subscriptions.push(disposable); - - - }).then(undefined, console.error); - - //side bar extension - info and vcs - // const ljProvider = new LiquidJavaProvider(vscode.workspace.rootPath, context); - // vscode.window.registerTreeDataProvider('liquidJava', ljProvider); - // vscode.commands.registerCommand('liquidJava.start', () => ljProvider.start()); - -} - - -// this method is called when your extension is deactivated -export function deactivate() { - console.log('Your extension "liquidjava" is now deactivated!'); -} - - - - - - - - -// MIT Licensed code from: https://github.com/georgewfraser/vscode-javac -function findJavaExecutable(binname: string) { - binname = correctBinname(binname); - - // First search each JAVA_HOME bin folder - if (process.env['JAVA_HOME']) { - const workspaces = process.env['JAVA_HOME'].split(path.delimiter); - for (let i = 0; i < workspaces.length; i++) { - const binpath = path.join(workspaces[i], 'bin', binname); - if (fs.existsSync(binpath)) - return binpath; - } - } - - // Then search PATH parts - if (process.env['PATH']) { - const pathparts = process.env['PATH'].split(path.delimiter); - for (let i = 0; i < pathparts.length; i++) { - const binpath = path.join(pathparts[i], binname); - if (fs.existsSync(binpath)) { - return binpath; - } - } - } - - // Else return the binary name directly (this will likely always fail downstream) - return null; -} - -function correctBinname(binname: string) { - if (process.platform === 'win32') - return binname + '.exe'; - else - return binname; -} - - - - - - - - - - - - - - - - - - - - - - -// import * as vscode from 'vscode'; -// import * as fs from "fs"; -// import * as path from 'path'; -// import {Trace} from 'vscode-jsonrpc'; -// import * as net from 'net'; -// import * as child_process from "child_process"; - -// import { workspace, Disposable, ExtensionContext } from 'vscode'; -// import { LanguageClient, LanguageClientOptions, SettingMonitor, StreamInfo } from 'vscode-languageclient'; - -// import { LiquidJavaProvider } from './liquidJavaProvider'; - -// export function activate(context: vscode.ExtensionContext) { - -// // The server is a started as a separate app and listens on port 1278 -// let serverOptions; -// if(process.arch != 'x32' && process.arch != 'x64') { -// throw 'unsupported CPU, this extension only runs on windows, macos or linux on x86 CPUs: ' + process.arch -// } -// serverOptions = { -// run: { -// command: path.join(context.extensionPath, '..', 'server', 'build', 'libs', 'language-server-liquidjava.jar') -// }, -// debug: { -// command: path.join(context.extensionPath , '..', 'server', 'build', 'libs', 'language-server-liquidjava.jar') -// } -// }; - -// // else if(process.platform == 'darwin') { -// // serverOptions = { -// // run: { -// // command: path.join(context.extensionPath , 'lsp-images', 'archie-lsp-macos', 'bin', 'archie-lsp') -// // }, -// // debug: { -// // command: path.join(context.extensionPath , 'lsp-images', 'archie-lsp-macos', 'bin', 'archie-lsp') -// // } -// // }; -// // } else if (process.platform == 'linux') { -// // serverOptions = { -// // run: { -// // command: path.join(context.extensionPath , 'lsp-images', 'archie-lsp-linux-x64', 'bin', 'archie-lsp') -// // }, -// // debug: { -// // command: path.join(context.extensionPath , 'lsp-images', 'archie-lsp-linux-x64', 'bin', 'archie-lsp') -// // } -// // }; -// // } else { -// // throw 'unsupported platform, this extension only runs on windows, macos or linux on x86 CPUs: ' + process.platform -// // } - -// let clientOptions: LanguageClientOptions = { -// documentSelector: ['java'], -// synchronize: { -// fileEvents: workspace.createFileSystemWatcher('**/*.java') -// } -// }; - -// // Create the language client and start the client. -// let lc = new LanguageClient('LiquidJava Server', serverOptions, clientOptions); - -// lc.trace = Trace.Verbose; - -// let disposable = lc.start(); - -// // Push the disposable to the context's subscriptions so that the -// // client can be deactivated on extension deactivation -// context.subscriptions.push(disposable); - -// // let port:string = "50000"; -// // //Code bellow from vscode-languageserver-java-example [https://github.com/adamvoss/vscode-languageserver-java-example] -// // /** -// // * Connect to Server -// // * @param context -// // * @returns -// // */ -// // function createServer(): Promise { -// // return new Promise((resolve, reject) => { -// // var server = net.createServer((socket) => { -// // console.log("Creating server"); - -// // resolve({ -// // reader: socket, -// // writer: socket -// // }); - -// // socket.on('end', () => console.log("Disconnected")); -// // }).on('error', (err) => { -// // // handle errors here -// // throw err; -// // }); - -// // let javaExecutablePath = findJavaExecutable('java'); - -// // // grab a random port. -// // server.listen(() => { -// // // Start the child java process -// // let options = { cwd: workspace.rootPath }; - -// // let args = [ -// // '-jar', -// // path.resolve(context.extensionPath, '..', 'server', 'language-server-liquidjava.jar'), -// // port -// // ] - -// // console.log("ARGS"); -// // console.log(args); - -// // let process = child_process.spawn(javaExecutablePath, args, options); - -// // // Send raw output to a file -// // if (!fs.existsSync(context.storagePath)) -// // fs.mkdirSync(context.storagePath); - -// // let logFile = context.storagePath + '/vscode-languageserver-liquidjava-example.log'; -// // let logStream = fs.createWriteStream(logFile, { flags: 'w' }); - -// // process.stdout.pipe(logStream); -// // process.stderr.pipe(logStream); - -// // console.log(`Storing log in '${logFile}'`); -// // }); -// // }); -// // }; - - - -// // // Options to control the language client -// // let clientOptions: LanguageClientOptions = { -// // // Register the server for plain text documents -// // documentSelector:[{language:'java', scheme:"file"}],// ["plaintext"],// -// // synchronize: { -// // // Synchronize the setting section 'languageServerExample' to the server -// // configurationSection: 'liquidJavaServer', -// // // Notify the server about file changes to '.clientrc files contain in the workspace -// // fileEvents: workspace.createFileSystemWatcher('**/.clientrc') -// // } -// // } - -// // // Create the language client and start the client. -// // let disposable = new LanguageClient('liquidJavaServer', 'Language Server Example', createServer, clientOptions).start(); - -// // // Push the disposable to the context's subscriptions so that the -// // // client can be deactivated on extension deactivation -// // context.subscriptions.push(disposable); - - -// //side bar extension - info and vcs -// const ljProvider = new LiquidJavaProvider(vscode.workspace.rootPath, context); -// vscode.window.registerTreeDataProvider('liquidJava', ljProvider); -// vscode.commands.registerCommand('liquidJava.start', () => ljProvider.start()); - -// } - - - - - - - - - - -// // MIT Licensed code from: https://github.com/georgewfraser/vscode-javac -// function findJavaExecutable(binname: string) { -// binname = correctBinname(binname); - -// // First search each JAVA_HOME bin folder -// if (process.env['JAVA_HOME']) { -// let workspaces = process.env['JAVA_HOME'].split(path.delimiter); -// for (let i = 0; i < workspaces.length; i++) { -// let binpath = path.join(workspaces[i], 'bin', binname); -// if (fs.existsSync(binpath)) { -// return binpath; -// } -// } -// } - -// // Then search PATH parts -// if (process.env['PATH']) { -// let pathparts = process.env['PATH'].split(path.delimiter); -// for (let i = 0; i < pathparts.length; i++) { -// let binpath = path.join(pathparts[i], binname); -// if (fs.existsSync(binpath)) { -// return binpath; -// } -// } -// } - -// // Else return the binary name directly (this will likely always fail downstream) -// return null; -// } - -// function correctBinname(binname: string) { -// if (process.platform === 'win32') -// return binname + '.exe'; -// else -// return binname; -// } - - diff --git a/client/src/extension_test.ts b/client/src/extension_test.ts deleted file mode 100644 index 44ab77c..0000000 --- a/client/src/extension_test.ts +++ /dev/null @@ -1,173 +0,0 @@ -import * as vscode from 'vscode'; -import * as fs from "fs"; -import * as path from 'path'; -import {Trace} from 'vscode-jsonrpc'; -import * as net from 'net'; -import * as child_process from "child_process"; - -import { workspace, Disposable, ExtensionContext } from 'vscode'; -import { LanguageClient, LanguageClientOptions, ServerOptions, SettingMonitor, StreamInfo, TransportKind } from 'vscode-languageclient'; - - -import { LiquidJavaProvider } from './liquidJavaProvider'; -import { Executable } from 'vscode-languageclient/lib/client'; - -export function activate(context: vscode.ExtensionContext) { - const activeTextEditor = vscode.window.activeTextEditor; - const glob = '**/liquidjava-api*.jar';//or +'/{*.png,*.jpeg}'; - vscode.workspace.findFiles(glob, null, 100).then((uris: vscode.Uri[] ) => { - if(uris.length == 0){ - console.log("No references to liquidJava api in workspace"); - vscode.window.showInformationMessage("Not using LiquidJava - Api not in the workspace"); - return; - }else{ - vscode.window.showInformationMessage("Found LiquidJava Api in the workspace - Loading Extension..."); - } - uris.forEach((uri: vscode.Uri) => { - console.log("Found uri:"+uri); - }); - - - - // var port; - // var server = net.createServer( function (sock) { - // }); - // server.listen(0,function() { //'listening' listener - // var port = this.address().port; - // console.log('server bound:'+port); - // }); - - const ports = ["50000", "50101", "50202", "50303", "50404"]; - const random = Math.floor(Math.random() * ports.length); - - const chosenPort = ports[random]; - console.log("Port:" + chosenPort); - - const connectionInfo = { - port: parseInt(chosenPort) - }; - - const javaExecutablePath = findJavaExecutable('java'); - const args = [ - '-jar', - path.resolve(context.extensionPath, 'server', 'vscode-liquid-java-server-0.0.2-SNAPSHOT-jar-with-dependencies.jar'), - chosenPort - ]; - const options = { - cwd: workspace.rootPath - // timeout:setTimeout(null, 3000) - }; - const process = child_process.spawn(javaExecutablePath, args, options); - - let first = true; - let firstVerification = true; - process.stdout.on('data', (data) => { - console.log(`stdout: ${data}`); - let st = data.toString(); - st = st.substring(0, 5); - - if(st == "Ready" && first){ - console.log("Server is ON! Starting Client!"); - - const serverOptions = () => { - // Connect to language server via socket - const socket = net.connect(connectionInfo); - const result: StreamInfo = { - writer: socket, - reader: socket - }; - return Promise.resolve(result); - }; - - // Options to control the language client - const clientOptions: LanguageClientOptions = { - documentSelector: ['java'] - }; - const disposable = new LanguageClient('liquidJavaServer','LiquidJava Server', serverOptions, clientOptions).start(); - console.log("Created LanguageClient"); - // Disposables to remove on deactivation. - context.subscriptions.push(disposable); - - first = !first; - } - - let onVerification = data.toString(); - onVerification = onVerification.substring(0,14); - if(onVerification == "OnVerification" && firstVerification){ - vscode.window.showInformationMessage("LiquidJava Extension is ON! Enjoy!"); - firstVerification = !firstVerification; - } - - }); - - process.stderr.on('data', (data) => { - console.error(`stderr: ${data}`); - }); - - - process.on('close', (code) => { - console.log(`Child process exited with code ${code}`); - }); - - process.on('error', (err) => { - console.error('Failed to start subprocess.'); - }); - - process.on('connection', (socket) => { - console.error('On connection'); - }); - - - - }).then(undefined, console.error); - - //side bar extension - info and vcs - // const ljProvider = new LiquidJavaProvider(vscode.workspace.rootPath, context); - // vscode.window.registerTreeDataProvider('liquidJava', ljProvider); - // vscode.commands.registerCommand('liquidJava.start', () => ljProvider.start()); - -} - - -// this method is called when your extension is deactivated -export function deactivate() { - console.log('Your extension "liquidjava" is now deactivated!'); -} - - -// MIT Licensed code from: https://github.com/georgewfraser/vscode-javac -function findJavaExecutable(binname: string) { - binname = correctBinname(binname); - - // First search each JAVA_HOME bin folder - if (process.env['JAVA_HOME']) { - const workspaces = process.env['JAVA_HOME'].split(path.delimiter); - for (let i = 0; i < workspaces.length; i++) { - const binpath = path.join(workspaces[i], 'bin', binname); - if (fs.existsSync(binpath)) { - return binpath; - } - } - } - - // Then search PATH parts - if (process.env['PATH']) { - const pathparts = process.env['PATH'].split(path.delimiter); - for (let i = 0; i < pathparts.length; i++) { - const binpath = path.join(pathparts[i], binname); - if (fs.existsSync(binpath)) { - return binpath; - } - } - } - - // Else return the binary name directly (this will likely always fail downstream) - return null; -} - -function correctBinname(binname: string) { - if (process.platform === 'win32') - return binname + '.exe'; - else - return binname; -} diff --git a/client/src/liquidJavaProvider.ts b/client/src/liquidJavaProvider.ts index 6b1f663..cadfdb3 100644 --- a/client/src/liquidJavaProvider.ts +++ b/client/src/liquidJavaProvider.ts @@ -1,128 +1,127 @@ -import * as vscode from 'vscode'; -import * as fs from 'fs'; -import * as path from 'path'; -import { TreeLiquidJava } from './treeLiquidJava'; - - -const tree: { [key: string]: string[] } = { - 'positive_grade_1 == 55': - ['Variable: positive_grade_1', 'Created in: int positive grade = 55', 'File: Grade.java:22, 29'], - 'bonus_2 == op1': - ['Variable: bonus_2', 'Created in: 10 + 5','File: Grade.java:24, 29'], - 'op1 == ( 10 + 5 )':[ - 'Variable: op1','Created in: 10 + 5.0', 'File: Grade.java:24, 29.'] -}; - -export class LiquidJavaProvider implements vscode.TreeDataProvider { - n = 0; - private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); - readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; - private vcs:TreeLiquidJava; - - constructor(private workspaceRoot: string, context: vscode.ExtensionContext) { - //Already have string for workspace - this.vcs = new TreeLiquidJava(context, tree); - } - - start(): void { - this._onDidChangeTreeData.fire(); - tree["new_"+this.n] = ["new_child_"+this.n]; - this.n++; - this.vcs.showNewTree(tree); - } - - getTreeItem(element: Dependency): vscode.TreeItem { - return element; - } - - getChildren(element?: Dependency): Thenable { - if (!this.workspaceRoot) { - vscode.window.showInformationMessage('No dependency in empty workspace'); - return Promise.resolve([]); - } - - vscode.window.showInformationMessage('Root Path:'+this.workspaceRoot); - const r = [new Dependency("Failed to check refinement at:"), - new Dependency("addBonus(positive_grade, (10 + 50)):"), - new Dependency("Type Expected:"), - new Dependency("(Percentage(#bonus_2) && (#bonus_2 < #positive_grade_1))") - ]; - return Promise.resolve(r); - - // if (element) { - // return Promise.resolve(this.getDepsInPackageJson(path.join(this.workspaceRoot, 'node_modules', element.label, 'package.json'))); - // } else { - // const packageJsonPath = path.join(this.workspaceRoot, 'package.json'); - // if (this.pathExists(packageJsonPath)) { - // return Promise.resolve(this.getDepsInPackageJson(packageJsonPath)); - // } else { - // vscode.window.showInformationMessage('Workspace has no package.json'); - // return Promise.resolve([]); - // } - // } - - } - - /** - * Given the path to package.json, read all its dependencies and devDependencies. - */ - // private getDepsInPackageJson(packageJsonPath: string): Dependency[] { - // if (this.pathExists(packageJsonPath)) { - // const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); - - // const toDep = (moduleName: string, version: string): Dependency => { - // if (this.pathExists(path.join(this.workspaceRoot, 'node_modules', moduleName))) { - // return new Dependency(moduleName, version, vscode.TreeItemCollapsibleState.Collapsed); - // } else { - // return new Dependency(moduleName, version, vscode.TreeItemCollapsibleState.None, { - // command: 'extension.openPackageOnNpm', - // title: '', - // arguments: [moduleName] - // }); - // } - // }; - - // const deps = packageJson.dependencies - // ? Object.keys(packageJson.dependencies).map(dep => toDep(dep, packageJson.dependencies[dep])) - // : []; - // const devDeps = packageJson.devDependencies - // ? Object.keys(packageJson.devDependencies).map(dep => toDep(dep, packageJson.devDependencies[dep])) - // : []; - // return deps.concat(devDeps); - // } else { - // return []; - // } - // } - - // private pathExists(p: string): boolean { - // try { - // fs.accessSync(p); - // } catch (err) { - // return false; - // } - - // return true; - // } -} - -export class Dependency extends vscode.TreeItem { - - constructor( - public readonly label: string, - public readonly collapsibleState?: vscode.TreeItemCollapsibleState, - private readonly version?: string, - public readonly command?: vscode.Command - ) { - super(label, undefined); - - this.tooltip = `${this.label}-${this.version}`; - this.description = this.version; - } - - iconPath = { - light: path.join(__filename, '..', '..', 'resources', 'light', 'dependency.svg'), - dark: path.join(__filename, '..', '..', 'resources', 'dark', 'dependency.svg') - }; - - contextValue = 'dependency'; -} +// import * as vscode from 'vscode'; +// import * as fs from 'fs'; +// import * as path from 'path'; +// import { TreeLiquidJava } from './treeLiquidJava'; + +// const tree: { [key: string]: string[] } = { +// 'positive_grade_1 == 55': +// ['Variable: positive_grade_1', 'Created in: int positive grade = 55', 'File: Grade.java:22, 29'], +// 'bonus_2 == op1': +// ['Variable: bonus_2', 'Created in: 10 + 5','File: Grade.java:24, 29'], +// 'op1 == ( 10 + 5 )':[ +// 'Variable: op1','Created in: 10 + 5.0', 'File: Grade.java:24, 29.'] +// }; + +// export class LiquidJavaProvider implements vscode.TreeDataProvider { +// n = 0; +// private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); +// readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; +// private vcs:TreeLiquidJava; + +// constructor(private workspaceRoot: string, context: vscode.ExtensionContext) { +// //Already have string for workspace +// this.vcs = new TreeLiquidJava(context, tree); +// } + +// start(): void { +// this._onDidChangeTreeData.fire(); +// tree["new_"+this.n] = ["new_child_"+this.n]; +// this.n++; +// this.vcs.showNewTree(tree); +// } + +// getTreeItem(element: Dependency): vscode.TreeItem { +// return element; +// } + +// getChildren(element?: Dependency): Thenable { +// if (!this.workspaceRoot) { +// vscode.window.showInformationMessage('No dependency in empty workspace'); +// return Promise.resolve([]); +// } + +// vscode.window.showInformationMessage('Root Path:'+this.workspaceRoot); +// const r = [new Dependency("Failed to check refinement at:"), +// new Dependency("addBonus(positive_grade, (10 + 50)):"), +// new Dependency("Type Expected:"), +// new Dependency("(Percentage(#bonus_2) && (#bonus_2 < #positive_grade_1))") +// ]; +// return Promise.resolve(r); + +// // if (element) { +// // return Promise.resolve(this.getDepsInPackageJson(path.join(this.workspaceRoot, 'node_modules', element.label, 'package.json'))); +// // } else { +// // const packageJsonPath = path.join(this.workspaceRoot, 'package.json'); +// // if (this.pathExists(packageJsonPath)) { +// // return Promise.resolve(this.getDepsInPackageJson(packageJsonPath)); +// // } else { +// // vscode.window.showInformationMessage('Workspace has no package.json'); +// // return Promise.resolve([]); +// // } +// // } + +// } + +// /** +// * Given the path to package.json, read all its dependencies and devDependencies. +// */ +// // private getDepsInPackageJson(packageJsonPath: string): Dependency[] { +// // if (this.pathExists(packageJsonPath)) { +// // const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); + +// // const toDep = (moduleName: string, version: string): Dependency => { +// // if (this.pathExists(path.join(this.workspaceRoot, 'node_modules', moduleName))) { +// // return new Dependency(moduleName, version, vscode.TreeItemCollapsibleState.Collapsed); +// // } else { +// // return new Dependency(moduleName, version, vscode.TreeItemCollapsibleState.None, { +// // command: 'extension.openPackageOnNpm', +// // title: '', +// // arguments: [moduleName] +// // }); +// // } +// // }; + +// // const deps = packageJson.dependencies +// // ? Object.keys(packageJson.dependencies).map(dep => toDep(dep, packageJson.dependencies[dep])) +// // : []; +// // const devDeps = packageJson.devDependencies +// // ? Object.keys(packageJson.devDependencies).map(dep => toDep(dep, packageJson.devDependencies[dep])) +// // : []; +// // return deps.concat(devDeps); +// // } else { +// // return []; +// // } +// // } + +// // private pathExists(p: string): boolean { +// // try { +// // fs.accessSync(p); +// // } catch (err) { +// // return false; +// // } + +// // return true; +// // } +// } + +// export class Dependency extends vscode.TreeItem { + +// constructor( +// public readonly label: string, +// public readonly collapsibleState?: vscode.TreeItemCollapsibleState, +// private readonly version?: string, +// public readonly command?: vscode.Command +// ) { +// super(label, undefined); + +// this.tooltip = `${this.label}-${this.version}`; +// this.description = this.version; +// } + +// iconPath = { +// light: path.join(__filename, '..', '..', 'resources', 'light', 'dependency.svg'), +// dark: path.join(__filename, '..', '..', 'resources', 'dark', 'dependency.svg') +// }; + +// contextValue = 'dependency'; +// } diff --git a/client/src/treeLiquidJava copy.ts b/client/src/treeLiquidJava copy.ts deleted file mode 100644 index 25638ed..0000000 --- a/client/src/treeLiquidJava copy.ts +++ /dev/null @@ -1,124 +0,0 @@ -import * as vscode from 'vscode'; - - -export class TreeLiquidJava { - - constructor(context: vscode.ExtensionContext) { - const view = vscode.window.createTreeView('liquidJavaVCS', { treeDataProvider: aNodeWithIdTreeDataProvider(), showCollapseAll: true }); - context.subscriptions.push(view); - vscode.commands.registerCommand('liquidJavaVCS.reveal', async () => { - const key = await vscode.window.showInputBox({ placeHolder: 'Type the label of the item to reveal' }); - if (key) { - await view.reveal({ key }, { focus: true, select: false, expand: true }); - } - }); - // vscode.commands.registerCommand('testView.changeTitle', async () => { - // const title = await vscode.window.showInputBox({ prompt: 'Type the new title for the Test View', placeHolder: view.title }); - // if (title) { - // view.title = title; - // } - // }); - } -} - - - -const tree: { [key: string]: string[] } = { - 'positive_grade_1 == 55': - ['Variable: positive_grade_1', 'Created in: int positive grade = 55', 'File: Grade.java:22, 29'] - , - 'bonus_2 == op1': - ['Variable: bonus_2', - 'Created in: 10 + 5', - 'File: Grade.java:24, 29'], - 'op1 == ( 10 + 5 )':[ - 'Variable: op1', - 'Created in: 10 + 5', - 'File: Grade.java:24, 28'] - // 'a': { - // 'aa': { - // 'aaa': { - // 'aaaa': { - // 'aaaaa': { - // 'aaaaaa': { - - // } - // } - // } - // } - // }, - // 'ab': {} - // }, - // 'b': { - // 'ba': {}, - // 'bb': {} - // } -}; -const nodes = {}; - -function aNodeWithIdTreeDataProvider(): vscode.TreeDataProvider<{ key: string }> { - return { - getChildren: (element: { key: string }): { key: string }[] => { - return getChildren(element ? element.key : undefined).map(key => getNode(key)); - }, - getTreeItem: (element: { key: string }): vscode.TreeItem => { - const treeItem = getTreeItem(element.key); - treeItem.id = element.key; - return treeItem; - }, - getParent: ({ key }: { key: string }): { key: string } => { - const parentKey = key.substring(0, key.length - 1); - return parentKey ? new Key(parentKey) : void 0; - } - }; -} - -function getChildren(key: string): string[] { - if (!key) { - return Object.keys(tree); - } - const treeElement = getTreeElement(key); - if (treeElement) { - return treeElement; - // return Object.keys(treeElement); - } - return []; -} - -function getTreeItem(key: string): vscode.TreeItem { - const treeElement = getTreeElement(key); - // An example of how to use codicons in a MarkdownString in a tree item tooltip. - const tooltip = new vscode.MarkdownString(`$(zap) Tooltip for ${key}`, true); - return { - label: key, - // label: /**vscode.TreeItemLabel**/{ label: key, highlights: key.length > 1 ? [[key.length - 2, key.length - 1]] : void 0 }, - // tooltip, - collapsibleState: treeElement && Object.keys(treeElement).length ? vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None - }; -} - -function getTreeElement(element): string[] { - const parent = tree; - if(element in parent) - return parent[element]; - return null; - // for (let i = 0; i < element.length; i++) { - // let c = element.substring(0, i + 1); - // parent = parent[element.substring(0, i + 1)]; - // if (!parent) { - // return null; - // } - // } - // return parent; -} - -function getNode(key: string): { key: string } { - if (!nodes[key]) { - nodes[key] = new Key(key); - } - return nodes[key]; -} - -class Key { - constructor(readonly key: string) { } -} \ No newline at end of file diff --git a/client/src/treeLiquidJava.ts b/client/src/treeLiquidJava.ts index 2698cd9..f450043 100644 --- a/client/src/treeLiquidJava.ts +++ b/client/src/treeLiquidJava.ts @@ -1,101 +1,101 @@ -import * as vscode from 'vscode'; +// import * as vscode from 'vscode'; -let tree: { [key: string]: string[] }; +// let tree: { [key: string]: string[] }; -export class TreeLiquidJava { - constructor(context: vscode.ExtensionContext, tr: { [key: string]: string[] }) { - tree = tr; - const view = vscode.window.createTreeView('liquidJavaVCS', { treeDataProvider: aNodeWithIdTreeDataProvider(), showCollapseAll: true }); - context.subscriptions.push(view); - vscode.commands.registerCommand('liquidJavaVCS.reveal', async () => { - const key = await vscode.window.showInputBox({ placeHolder: 'Type the label of the item to reveal' }); - if (key) { - await view.reveal({ key }, { focus: true, select: false, expand: true }); - } - }); - // vscode.commands.registerCommand('testView.changeTitle', async () => { - // const title = await vscode.window.showInputBox({ prompt: 'Type the new title for the Test View', placeHolder: view.title }); - // if (title) { - // view.title = title; - // } - // }); - } +// export class TreeLiquidJava { +// constructor(context: vscode.ExtensionContext, tr: { [key: string]: string[] }) { +// tree = tr; +// const view = vscode.window.createTreeView('liquidJavaVCS', { treeDataProvider: aNodeWithIdTreeDataProvider(), showCollapseAll: true }); +// context.subscriptions.push(view); +// vscode.commands.registerCommand('liquidJavaVCS.reveal', async () => { +// const key = await vscode.window.showInputBox({ placeHolder: 'Type the label of the item to reveal' }); +// if (key) { +// await view.reveal({ key }, { focus: true, select: false, expand: true }); +// } +// }); +// // vscode.commands.registerCommand('testView.changeTitle', async () => { +// // const title = await vscode.window.showInputBox({ prompt: 'Type the new title for the Test View', placeHolder: view.title }); +// // if (title) { +// // view.title = title; +// // } +// // }); +// } - showNewTree(tr: { [key: string]: string[] }):void{ - tree = tr; - const view = vscode.window.createTreeView('liquidJavaVCS', { treeDataProvider: aNodeWithIdTreeDataProvider(), showCollapseAll: true }); - // context.subscriptions.push(view); - } -} +// showNewTree(tr: { [key: string]: string[] }):void{ +// tree = tr; +// const view = vscode.window.createTreeView('liquidJavaVCS', { treeDataProvider: aNodeWithIdTreeDataProvider(), showCollapseAll: true }); +// // context.subscriptions.push(view); +// } +// } -const nodes = {}; +// const nodes = {}; -function aNodeWithIdTreeDataProvider(): vscode.TreeDataProvider<{ key: string }> { - return { - getChildren: (element: { key: string }): { key: string }[] => { - return getChildren(element ? element.key : undefined).map(key => getNode(key)); - }, - getTreeItem: (element: { key: string }): vscode.TreeItem => { - const treeItem = getTreeItem(element.key); - treeItem.id = element.key; - return treeItem; - }, - getParent: ({ key }: { key: string }): { key: string } => { - const parentKey = key.substring(0, key.length - 1); - return parentKey ? new Key(parentKey) : void 0; - } - }; -} +// function aNodeWithIdTreeDataProvider(): vscode.TreeDataProvider<{ key: string }> { +// return { +// getChildren: (element: { key: string }): { key: string }[] => { +// return getChildren(element ? element.key : undefined).map(key => getNode(key)); +// }, +// getTreeItem: (element: { key: string }): vscode.TreeItem => { +// const treeItem = getTreeItem(element.key); +// treeItem.id = element.key; +// return treeItem; +// }, +// getParent: ({ key }: { key: string }): { key: string } => { +// const parentKey = key.substring(0, key.length - 1); +// return parentKey ? new Key(parentKey) : void 0; +// } +// }; +// } -function getChildren(key: string): string[] { - if (!key) { - return Object.keys(tree); - } - const treeElement = getTreeElement(key); - if (treeElement) { - return treeElement; - // return Object.keys(treeElement); - } - return []; -} +// function getChildren(key: string): string[] { +// if (!key) { +// return Object.keys(tree); +// } +// const treeElement = getTreeElement(key); +// if (treeElement) { +// return treeElement; +// // return Object.keys(treeElement); +// } +// return []; +// } -function getTreeItem(key: string): vscode.TreeItem { - const treeElement = getTreeElement(key); - // An example of how to use codicons in a MarkdownString in a tree item tooltip. - const tooltip = new vscode.MarkdownString(`$(zap) Tooltip for ${key}`, true); - return { - label: key, - // label: /**vscode.TreeItemLabel**/{ label: key, highlights: key.length > 1 ? [[key.length - 2, key.length - 1]] : void 0 }, - // tooltip, - collapsibleState: treeElement && Object.keys(treeElement).length ? vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None - }; -} +// function getTreeItem(key: string): vscode.TreeItem { +// const treeElement = getTreeElement(key); +// // An example of how to use codicons in a MarkdownString in a tree item tooltip. +// const tooltip = new vscode.MarkdownString(`$(zap) Tooltip for ${key}`, true); +// return { +// label: key, +// // label: /**vscode.TreeItemLabel**/{ label: key, highlights: key.length > 1 ? [[key.length - 2, key.length - 1]] : void 0 }, +// // tooltip, +// collapsibleState: treeElement && Object.keys(treeElement).length ? vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None +// }; +// } -function getTreeElement(element): string[] { - const parent = tree; - if(element in parent) - return parent[element]; - return null; - // for (let i = 0; i < element.length; i++) { - // let c = element.substring(0, i + 1); - // parent = parent[element.substring(0, i + 1)]; - // if (!parent) { - // return null; - // } - // } - // return parent; -} +// function getTreeElement(element): string[] { +// const parent = tree; +// if(element in parent) +// return parent[element]; +// return null; +// // for (let i = 0; i < element.length; i++) { +// // let c = element.substring(0, i + 1); +// // parent = parent[element.substring(0, i + 1)]; +// // if (!parent) { +// // return null; +// // } +// // } +// // return parent; +// } -function getNode(key: string): { key: string } { - if (!nodes[key]) { - nodes[key] = new Key(key); - } - return nodes[key]; -} +// function getNode(key: string): { key: string } { +// if (!nodes[key]) { +// nodes[key] = new Key(key); +// } +// return nodes[key]; +// } -class Key { - constructor(readonly key: string) { } -} \ No newline at end of file +// class Key { +// constructor(readonly key: string) { } +// } \ No newline at end of file diff --git a/client/src/utils.ts b/client/src/utils.ts new file mode 100644 index 0000000..668bf89 --- /dev/null +++ b/client/src/utils.ts @@ -0,0 +1,135 @@ +import * as vscode from "vscode"; +import * as fs from "fs"; +import * as path from "path"; +import * as net from "net"; +import * as child_process from "child_process"; +import { API_JAR_GLOB } from "./constants"; + +/** + * Checks if the extension can be activated by looking for the LiquidJava API jar in the workspace + * @returns true if the extension can be activated, false otherwise + */ +export async function isJarPresent(): Promise { + const uris = await vscode.workspace.findFiles(API_JAR_GLOB); + return uris.length > 0; +} + +/** + * Finds the Java executable in the system, either in JAVA_HOME or in PATH + * MIT Licensed code from: https://github.com/georgewfraser/vscode-javac + */ +export function findJavaExecutable(binname: string): string | null { + binname = process.platform === "win32" ? `${binname}.exe` : binname; + + // First search each JAVA_HOME bin folder + if (process.env["JAVA_HOME"]) { + for (const workspace of process.env["JAVA_HOME"].split(path.delimiter)) { + const binpath = path.join(workspace, "bin", binname); + if (fs.existsSync(binpath)) return binpath; + } + } + // Then search PATH parts + if (process.env["PATH"]) { + for (const part of process.env["PATH"].split(path.delimiter)) { + const binpath = path.join(part, binname); + if (fs.existsSync(binpath)) return binpath; + } + } + // Else return null + return null; +} + +/** + * Gets an available port in the OS + * @returns A promise to the available port number + */ +export async function getAvailablePort(): Promise { + return new Promise((resolve, reject) => { + const server = net.createServer(); + server.listen(0, "localhost", () => { + const port = (server.address() as net.AddressInfo).port; + server.close(); + resolve(port); + }); + server.on("error", reject); + }); +} + +/** + * Connects to the process on the given port, retrying until timeout + * @param port The port to connect to + * @param timeout The timeout duration in milliseconds + * @param attemptInterval The interval between connection attempts in milliseconds + * @param connectionTimeout The timeout for each individual connection attempt in milliseconds + * @returns A promise to the connected socket + */ +export async function connectToPort( + port: number, + timeout = 10000, + attemptInterval = 500, + connectionTimeout = 500 +): Promise { + const deadline = Date.now() + timeout; + while (Date.now() < deadline) { + try { + return await new Promise((resolve, reject) => { + const s = net.connect({ port }); + + // connection timeout + const t = setTimeout(() => { + s.destroy(new Error("connection timeout")); + reject(new Error("connection timeout")); + }, connectionTimeout); + + // success + s.once("connect", () => { + clearTimeout(t); + resolve(s); + }); + + // failure + s.once("error", (e) => { + clearTimeout(t); + reject(e); + }); + }); + } catch { + // wait and retry + await new Promise((r) => setTimeout(r, attemptInterval)); + } + } + throw new Error(`Server not reachable on port ${port} within ${timeout}ms`); +} + +/** + * Kills the given process if it is running + * @param proc The process to kill + * @returns A promise that resolves when the process has been killed + */ +export async function killProcess(proc?: child_process.ChildProcess) { + return new Promise((resolve, reject) => { + if (!proc || proc.killed) { + // already killed + resolve(); + return; + } + if (process.platform === "win32") { + // Windows + child_process.exec(`taskkill /pid ${proc.pid} /T /F`, (err) => { + if (err) { + reject(err); + } else { + resolve(); + } + }); + } else { + // Unix + try { + process.kill(proc.pid, "SIGKILL"); + resolve(); + } catch (err) { + reject(err); + } + } + }); +} diff --git a/server/libs/liquidjava-verifier-5.2-SNAPSHOT.jar b/server/libs/liquidjava-verifier.jar similarity index 97% rename from server/libs/liquidjava-verifier-5.2-SNAPSHOT.jar rename to server/libs/liquidjava-verifier.jar index d63eee7..13990e8 100644 Binary files a/server/libs/liquidjava-verifier-5.2-SNAPSHOT.jar and b/server/libs/liquidjava-verifier.jar differ diff --git a/server/pom.xml b/server/pom.xml index 6a06c86..3cc4d15 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -137,7 +137,7 @@ verifier 5.2-SNAPSHOT system - ${project.basedir}/libs/liquidjava-verifier-5.2-SNAPSHOT.jar + ${project.basedir}/libs/liquidjava-verifier.jar tools.aqua diff --git a/server/src/main/java/LJLanguageServer.java b/server/src/main/java/LJLanguageServer.java index c6b1d8b..1417efc 100644 --- a/server/src/main/java/LJLanguageServer.java +++ b/server/src/main/java/LJLanguageServer.java @@ -9,6 +9,7 @@ import org.eclipse.lsp4j.WorkspaceFoldersOptions; import org.eclipse.lsp4j.WorkspaceServerCapabilities; import org.eclipse.lsp4j.jsonrpc.RemoteEndpoint; +import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; import org.eclipse.lsp4j.services.LanguageClient; import org.eclipse.lsp4j.services.LanguageServer; import org.eclipse.lsp4j.services.TextDocumentService; @@ -69,4 +70,9 @@ public WorkspaceService getWorkspaceService() { public void connect(LanguageClient remoteProxy, RemoteEndpoint remoteEndpoint) { textDocumentService.setClient(remoteProxy); } + + @JsonNotification("$/setTraceNotification") + public void setTrace(Object params) { + // suppress notification + } }