diff --git a/package.json b/package.json index d66b5a4..6647a9f 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,8 @@ "onLanguage:JuvixAsm", "onLanguage:JuvixCore", "onLanguage:JuvixGeb", - "onLanguage:VampIR" + "onLanguage:VampIR", + "onCommand:juvix-mode.installJuvixBinary" ], "main": "./out/extension.js", "scripts": { @@ -486,6 +487,11 @@ } ], "commands": [ + { + "command": "juvix-mode.installJuvixBinary", + "title": "Install Juvix Binary", + "category": "Juvix" + }, { "command": "juvix-mode.getBinaryVersion", "title": "about", @@ -705,77 +711,77 @@ "menus": { "editor/title": [ { - "when": "editorLangId == Juvix || editorLangId == JuvixCore || editorLangId == JuvixGeb", + "when": "juvix-mode:ready && (editorLangId == Juvix || editorLangId == JuvixCore || editorLangId == JuvixGeb)", "command": "juvix-mode.loadFileRepl", "group": "navigation@0" }, { - "when": "editorLangId == Juvix", + "when": "juvix-mode:ready && editorLangId == Juvix", "command": "juvix-mode.typecheck", "group": "navigation@1" }, { - "when": "editorLangId == Juvix", + "when": "juvix-mode:ready && editorLangId == Juvix", "command": "juvix-mode.compile", "group": "navigation@2" }, { - "when": "editorLangId == Juvix", + "when": "juvix-mode:ready && editorLangId == Juvix", "command": "juvix-mode.run", "group": "navigation@3" }, { - "when": "editorLangId == JuvixCore", + "when": "juvix-mode:ready && editorLangId == JuvixCore", "command": "juvix-mode.core-compile", "group": "navigation@2" }, { - "when": "editorLangId == JuvixCore", + "when": "juvix-mode:ready && editorLangId == JuvixCore", "command": "juvix-mode.core-eval", "group": "navigation@3" }, { - "when": "editorLangId == JuvixGeb", + "when": "juvix-mode:ready && editorLangId == JuvixGeb", "command": "juvix-mode.geb-check", "group": "navigation@1" }, { - "when": "editorLangId == JuvixGeb", + "when": "juvix-mode:ready && editorLangId == JuvixGeb", "command": "juvix-mode.geb-compile", "group": "navigation@2" }, { - "when": "editorLangId == JuvixGeb", + "when": "juvix-mode:ready && editorLangId == JuvixGeb", "command": "juvix-mode.geb-eval", "group": "navigation@3" }, { - "when": "editorLangId == VampIR", + "when": "juvix-mode:ready && editorLangId == VampIR", "command": "juvix-mode.vampir-setup", "group": "navigation@0" }, { - "when": "editorLangId == VampIR", + "when": "juvix-mode:ready && editorLangId == VampIR", "command": "juvix-mode.vampir-compile", "group": "navigation@1" }, { - "when": "editorLangId == VampIR", + "when": "juvix-mode:ready && editorLangId == VampIR", "command": "juvix-mode.vampir-prove", "group": "navigation@2" }, { - "when": "editorLangId == VampIR", + "when": "juvix-mode:ready && editorLangId == VampIR", "command": "juvix-mode.vampir-verify", "group": "navigation@3" }, { - "when": "editorLangId == Juvix", + "when": "juvix-mode:ready && editorLangId == Juvix", "command": "juvix-mode.createOrShowJudoc", "group": "navigation@4" }, { - "when": "editorLangId == Juvix", + "when": "juvix-mode:ready && editorLangId == Juvix", "command": "juvix-mode.createOrShowJudocOnlySource", "group": "navigation@5" } @@ -784,7 +790,7 @@ { "submenu": "juvix-submenu", "group": "navigation", - "when": "editorLangId == Juvix" + "when": "juvix-mode:ready && editorLangId == Juvix" } ], "juvix-submenu": [ @@ -803,23 +809,27 @@ { "command": "juvix-mode.doctor", "group": "navigation@1.31" + }, + { + "command": "juvix-mode.createOrShowJudoc", + "group": "navigation@1.31" } ] }, "configuration": { "title": "Juvix Mode", "properties": { - "juvix-mode.bin.name": { + "juvix-mode.juvixBinName": { "type": "string", "default": "juvix", "scope": "machine-overridable", "description": "Name of the executable of Juvix. (e.g. juvix-v0.3.0)" }, - "juvix-mode.bin.path": { + "juvix-mode.juvixBinPath": { "type": "string", "default": "", "scope": "machine-overridable", - "description": "Absolute path to the executable of Juvix. (e.g. /usr/local/bin/)" + "description": "Absolute path where the Juvix binary is located (e.g. /usr/local/bin/)" }, "juvix-mode.vampirBinName": { "type": "string", diff --git a/src/config.ts b/src/config.ts index a0cba4a..b49ad0e 100644 --- a/src/config.ts +++ b/src/config.ts @@ -9,18 +9,23 @@ import * as fs from 'fs'; import * as path from 'path'; import { tmpdir } from 'os'; import { logger } from './utils/debug'; +import { ConfigurationTarget } from 'vscode'; export class JuvixConfig { - readonly binaryName = new VsCodeSetting('juvix-mode.bin.name', { - serializer: serializerWithDefault('Juvix'), + readonly binaryName = new VsCodeSetting('juvix-mode.juvixBinName', { + serializer: serializerWithDefault('juvix') }); - readonly binaryPath = new VsCodeSetting('juvix-mode.bin.path', { - serializer: serializerWithDefault(''), + readonly binaryPath = new VsCodeSetting('juvix-mode.juvixBinPath', { + serializer: serializerWithDefault('') }); public getJuvixExec(): string { - return path.join(this.binaryPath.get(), this.binaryName.get()); + let binPath = this.binaryPath.get(); + let binName = this.binaryName.get(); + logger.debug(`binPath: ${binPath}`); + logger.debug(`binName: ${binName}`); + return path.join(binPath, binName); } public setJuvixExec(juvixExec: string): void { @@ -31,9 +36,11 @@ export class JuvixConfig { // geb settings readonly vampirBinaryName = new VsCodeSetting('juvix-mode.vampirBinName', { serializer: serializerWithDefault('vamp-ir'), + target: ConfigurationTarget.Global, }); readonly vampirBinaryPath = new VsCodeSetting('juvix-mode.vampirBinPath', { serializer: serializerWithDefault(''), + target: ConfigurationTarget.Global, }); public getVampirExec(): string { @@ -43,30 +50,52 @@ export class JuvixConfig { // Geb settings readonly gebBinaryName = new VsCodeSetting('juvix-mode.gebName', { serializer: serializerWithDefault('geb.image'), + target: ConfigurationTarget.Global, }); readonly gebBinaryPath = new VsCodeSetting('juvix-mode.gebBinPath', { serializer: serializerWithDefault(''), + target: ConfigurationTarget.Global, }); public getGebExec(): string { return path.join(this.gebBinaryPath.get(), this.gebBinaryName.get()); } - readonly revealPanel = new VsCodeSetting('juvix-mode.revealPanel'); - readonly typecheckOn = new VsCodeSetting('juvix-mode.typecheckOn'); + readonly revealPanel = new VsCodeSetting('juvix-mode.revealPanel', { + target: ConfigurationTarget.Global, + }); + readonly typecheckOn = new VsCodeSetting('juvix-mode.typecheckOn', { + target: ConfigurationTarget.Global, + }); - readonly noColors = new VsCodeSetting('juvix-mode.opts.noColors'); + readonly noColors = new VsCodeSetting('juvix-mode.opts.noColors', { + target: ConfigurationTarget.Global, + }); - readonly showNameIds = new VsCodeSetting('juvix-mode.opts.showNameIds'); - readonly onlyErrors = new VsCodeSetting('juvix-mode.opts.onlyErrors'); - readonly noTermination = new VsCodeSetting('juvix-mode.opts.noTermination'); - readonly noPositivity = new VsCodeSetting('juvix-mode.opts.noPositivity'); - readonly noStdlib = new VsCodeSetting('juvix-mode.opts.noStdlib'); + readonly showNameIds = new VsCodeSetting('juvix-mode.opts.showNameIds', { + target: ConfigurationTarget.Global, + }); + readonly onlyErrors = new VsCodeSetting('juvix-mode.opts.onlyErrors', { + target: ConfigurationTarget.Global, + }); + readonly noTermination = new VsCodeSetting('juvix-mode.opts.noTermination', { + target: ConfigurationTarget.Global, + }); + readonly noPositivity = new VsCodeSetting('juvix-mode.opts.noPositivity', { + target: ConfigurationTarget.Global, + }); + readonly noStdlib = new VsCodeSetting('juvix-mode.opts.noStdlib', { + target: ConfigurationTarget.Global, + }); readonly internalBuildDir = new VsCodeSetting( - 'juvix-mode.opts.internalBuildDir' + 'juvix-mode.opts.internalBuildDir', + { + target: ConfigurationTarget.Global, + } ); - - readonly judocDir = new VsCodeSetting('juvix-mode.opts.judocDir'); + readonly judocDir = new VsCodeSetting('juvix-mode.opts.judocDir', { + target: ConfigurationTarget.Global, + }); public getInternalBuildDir(): string | undefined { const useTmpDir = () => { diff --git a/src/extension.ts b/src/extension.ts index bfab7e9..7d02c91 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -2,7 +2,6 @@ * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ 'use strict'; - import * as vscode from 'vscode'; import * as check from './check'; import * as codelens from './codelens'; @@ -18,24 +17,44 @@ import * as statusBar from './statusbar'; import * as tasks from './tasks'; import * as vampir from './vampir/tasks'; import * as installer from './installer'; -import { checkForUpgrade, checkJuvixBinary } from './juvixVersion'; +import { checkForUpgrade, checkJuvixBinary, juvixIsNotInstalled } from './juvixVersion'; +import { logger } from './utils/debug' +import { config } from './config'; export async function activate(context: vscode.ExtensionContext) { + logger.debug("Activating Juvix Mode"); + logger.debug("Initial config: " + config.getJuvixExec()); installer.activate(context); - checkJuvixBinary().then(version => { - statusBar.activate(context, version); + + let version = checkJuvixBinary(); + vscode.commands.executeCommand('setContext', "juvix-mode:ready", false); + + if (!version) { + logger.debug("Juvix binary not found, installing..."); + juvixIsNotInstalled().then(() => { + version = checkJuvixBinary(); + }); + } + if (version !== undefined) { + logger.debug("Juvix version detected: " + version); checkForUpgrade(version); - codelens.activate(context); - syntaxHighlighter.activate(context); - goToDefinition.activate(context); - hoverInfo.activate(context); - tasks.activate(context); - inputMethod.activate(context); - repl.activate(context); - judoc.activate(context); - check.activate(context); - formatter.activate(context); - vampir.activate(context); - dev.activate(context); - }); + statusBar.activate(context, version); + + const modules = [ + codelens, + syntaxHighlighter, + goToDefinition, + hoverInfo, + tasks, + inputMethod, + repl, + judoc, + check, + formatter, + vampir, + dev + ]; + modules.forEach(module => module.activate(context)); + vscode.commands.executeCommand('setContext', "juvix-mode:ready", true); + } } diff --git a/src/installer.ts b/src/installer.ts index e41a137..58bcc7e 100644 --- a/src/installer.ts +++ b/src/installer.ts @@ -6,6 +6,12 @@ import { exit } from 'process'; import * as vscode from 'vscode'; import { logger } from './utils/debug'; +import { config } from './config'; +import { env } from 'process'; +import * as path from 'path'; + +const userHome = env['XDG_BIN_HOME'] || env.HOME || '~'; +const INSTALLBIN_PATH = path.join(userHome, '.local', 'bin'); export class Installer { private terminal: vscode.Terminal; @@ -35,6 +41,7 @@ export class Installer { disposeToken.dispose(); if (this.terminal.exitStatus !== undefined) { resolve(this.terminal.exitStatus); + config.binaryPath.set(INSTALLBIN_PATH); vscode.window .showInformationMessage( 'Juvix binary installation complete.', @@ -42,9 +49,8 @@ export class Installer { ) .then(selection => { if (selection === 'Reload window') { - vscode.commands.executeCommand( - 'workbench.action.reloadWindow' - ); + vscode.window.terminals.forEach(t => t.dispose()); + vscode.commands.executeCommand( 'workbench.action.reloadWindow' ); } }); } else reject('Terminal exited with undefined status'); @@ -101,7 +107,7 @@ export async function installJuvix() { export async function activate(context: vscode.ExtensionContext) { context.subscriptions.push( - vscode.commands.registerCommand('juvix-mode.installBinary', () => { + vscode.commands.registerCommand('juvix-mode.installJuvixBinary', () => { installJuvix(); }) ); diff --git a/src/juvixVersion.ts b/src/juvixVersion.ts index 6fa562f..94625e0 100644 --- a/src/juvixVersion.ts +++ b/src/juvixVersion.ts @@ -2,7 +2,7 @@ * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ import { logger } from './utils/debug'; -import { JuvixConfig } from './config'; +import { config, JuvixConfig } from './config'; import * as versioning from 'compare-versions'; import * as fs from 'fs'; import * as path from 'path'; @@ -13,7 +13,8 @@ import { window } from 'vscode'; export async function juvixIsNotInstalled() { const juvixVer = 'Juvix-v' + supportedVersion; - const url = 'https://docs.juvix.org/' + supportedVersion + '/howto/installing/'; + const url = + 'https://docs.juvix.org/' + supportedVersion + '/howto/installing/'; const linkDocVersion = `[${url}](${url})`; const result = await window.showWarningMessage( @@ -29,8 +30,6 @@ export async function juvixIsNotInstalled() { if (result === 'Install') { await installJuvix(); } else { - // open the docs - logger.warn( 'Check the binary path in the configuration page or ' + `visit ${linkDocVersion} for instructions.` @@ -38,15 +37,25 @@ export async function juvixIsNotInstalled() { } } -export async function checkJuvixBinary(): Promise { - const config = new JuvixConfig(); - const ls = spawnSync(config.getJuvixExec(), ['--version']); - if (ls.status !== 0) await juvixIsNotInstalled(); - return ls.stdout.toString().replace('version ', 'v').split('\n')[0]; + +export function checkJuvixBinary(): string | undefined { + const juvixExec = config.getJuvixExec(); + logger.debug(juvixExec, "config.getJuvixExec()"); + try { + const ls = spawnSync(juvixExec, ['--version']); + if (ls.status !== 0) { + logger.debug('Juvix is not installed.', "checkJuvixBinary"); + return; + } + return ls.stdout.toString().replace('version ', 'v').split('\n')[0]; + } catch (e) { + logger.debug('Juvix is not installed.', "checkJuvixBinary"); + } } + + export function getInstalledNumericVersion(): string | undefined { - const config = new JuvixConfig(); const ls = spawnSync(config.getJuvixExec(), ['--numeric-version']); if (ls.status == 0) return ls.stdout.toString().split('\n')[0]; else juvixIsNotInstalled(); diff --git a/src/tasks.ts b/src/tasks.ts index 0e55e2c..310980a 100644 --- a/src/tasks.ts +++ b/src/tasks.ts @@ -20,7 +20,7 @@ export async function activate(context: vscode.ExtensionContext) { const msg = 'Juvix extension requires at least one workspace open.\n' + 'Open a folder containing a Juvix project and try again.'; - logger.error(msg, 'tasks.ts'); + logger.warn(msg, 'tasks.ts'); return; } diff --git a/src/vampir/tasks.ts b/src/vampir/tasks.ts index d9baa7d..3c4beb2 100644 --- a/src/vampir/tasks.ts +++ b/src/vampir/tasks.ts @@ -13,7 +13,7 @@ export async function activate(context: vscode.ExtensionContext) { if (vscode.workspace.workspaceFolders === undefined) { const msg = 'VampIR extension requires at least one workspace open.\n'; vscode.window.showErrorMessage(msg); - logger.error(msg); + logger.warn(msg); return; }