Skip to content

Commit

Permalink
✨ Spawn recipes in terminal (#42)
Browse files Browse the repository at this point in the history
* feat: first pass + general cleanup

* refactor: allow toggle of output channel vs terminal

* fix: delete dummy code
  • Loading branch information
nefrob authored Oct 14, 2024
1 parent 301e4cb commit 78d203f
Show file tree
Hide file tree
Showing 11 changed files with 230 additions and 68 deletions.
13 changes: 10 additions & 3 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
{
// Disable bracket colorization for testing
"editor.bracketPairColorization.enabled": false,
// Disable comment colorization for testing
"better-comments.tags": [],
"files.exclude": {
"dist": false,
"out": false
},
// Disable comment colorization for testing
"better-comments.tags": [],

"search.exclude": {
"yarn.lock": true
},
"search.useIgnoreFiles": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.fixAll.prettier": "explicit"
},
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
"typescript.tsc.autoDetect": "off",
"eslint.useFlatConfig": true,
"search.useIgnoreFiles": true
// have eslint auto sort imports
"eslint.format.enable": true
}
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@

- Add dependabot update configuration

### Changed

- Recipes can now be spawned in a terminal instead of being logged to the extension output channel
- Log level can now be set in the extension settings

## [0.6.0] - 2024-10-06

### Added

- Unicode codepoint escaped characters in strings from `just` release 1.36.0
- Unexport keyword from `just` release 1.29.0
- VSCode command to run recipes
- VSCode command to run recipes

## [0.5.3] - 2024-08-14

Expand Down
22 changes: 19 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"test-grammar": "vscode-tmgrammar-snap syntaxes/tests/**/*.just",
"format": "prettier --log-level warn --cache .",
"lint": "eslint . --cache --cache-location node_modules/.cache/eslint",
"gen-scopes": "yarn compile && node out/gen_scopes.js"
"gen-scopes": "yarn compile && node out/genScopes.js"
},
"categories": [
"Programming Languages"
Expand Down Expand Up @@ -69,12 +69,28 @@
"vscode-just.formatOnSave": {
"type": "boolean",
"default": false,
"description": "Enable/disable format on save (currently unstable)"
"description": "Enable/disable format on save (currently unstable)."
},
"vscode-just.justPath": {
"type": "string",
"default": "just",
"description": "Path to just binary"
"description": "Path to just binary."
},
"vscode-just.runInTerminal": {
"type": "boolean",
"default": false,
"description": "Enable/disable running recipes in a terminal. If disabled, recipes will be logged to the extension output channel instead."
},
"vscode-just.logLevel": {
"type": "string",
"enum": [
"info",
"warning",
"error",
"none"
],
"default": "info",
"description": "Set the log level. Recipe output is logged at the info level."
}
}
},
Expand Down
10 changes: 10 additions & 0 deletions src/const.ts
Original file line number Diff line number Diff line change
@@ -1 +1,11 @@
export const EXTENSION_NAME = 'vscode-just';
export const COMMANDS = {
formatDocument: `${EXTENSION_NAME}.formatDocument`,
runRecipe: `${EXTENSION_NAME}.runRecipe`,
};
export const SETTINGS = {
justPath: 'justPath',
formatOnSave: 'formatOnSave',
runInTerminal: 'runInTerminal',
logLevel: 'logLevel',
};
19 changes: 8 additions & 11 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import * as vscode from 'vscode';

import { EXTENSION_NAME } from './const';
import { COMMANDS, EXTENSION_NAME, SETTINGS } from './const';
import { formatWithExecutable } from './format';
import Logger from './logger';
import { getLauncher } from './launcher';
import { getLogger } from './logger';
import { runRecipeCommand } from './recipe';

export let LOGGER: Logger;

export const activate = (context: vscode.ExtensionContext) => {
console.debug(`${EXTENSION_NAME} activated`);
LOGGER = new Logger(EXTENSION_NAME);

const formatDisposable = vscode.commands.registerCommand(
`${EXTENSION_NAME}.formatDocument`,
COMMANDS.formatDocument,
() => {
const editor = vscode.window.activeTextEditor;
if (editor) {
Expand All @@ -23,7 +21,7 @@ export const activate = (context: vscode.ExtensionContext) => {
context.subscriptions.push(formatDisposable);

const runRecipeDisposable = vscode.commands.registerCommand(
`${EXTENSION_NAME}.runRecipe`,
COMMANDS.runRecipe,
async () => {
runRecipeCommand();
},
Expand All @@ -33,13 +31,12 @@ export const activate = (context: vscode.ExtensionContext) => {

export const deactivate = () => {
console.debug(`${EXTENSION_NAME} deactivated`);
if (LOGGER) {
LOGGER.dispose();
}
getLogger().dispose();
getLauncher().dispose();
};

vscode.workspace.onWillSaveTextDocument((event) => {
if (vscode.workspace.getConfiguration(EXTENSION_NAME).get('formatOnSave')) {
if (vscode.workspace.getConfiguration(EXTENSION_NAME).get(SETTINGS.formatOnSave)) {
formatWithExecutable(event.document.uri.fsPath);
}
});
12 changes: 5 additions & 7 deletions src/format.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { spawn } from 'child_process';
import * as vscode from 'vscode';

import { EXTENSION_NAME } from './const';
import { LOGGER } from './extension';
import { getLogger } from './logger';
import { getJustPath } from './utils';

const LOGGER = getLogger();

export const formatWithExecutable = (fsPath: string) => {
const justPath =
(vscode.workspace.getConfiguration(EXTENSION_NAME).get('justPath') as string) ||
'just';
const args = ['-f', fsPath, '--fmt', '--unstable'];

const childProcess = spawn(justPath, args);
const childProcess = spawn(getJustPath(), args);
childProcess.stdout.on('data', (data: string) => {
LOGGER.info(data);
});
Expand Down
File renamed without changes.
54 changes: 54 additions & 0 deletions src/launcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import * as vscode from 'vscode';

import { workspaceRoot } from './utils';

let LAUNCHER: Launcher;

class Launcher implements vscode.Disposable {
private terminals: Set<vscode.Terminal>;

private onTerminalClose = vscode.window.onDidCloseTerminal((terminal) => {
if (this.terminals.has(terminal)) {
this.terminals.delete(terminal);
}
});

constructor() {
this.terminals = new Set();
}

public launch(command: string, args: string[]) {
const terminalOptions: vscode.TerminalOptions = {
name: command,
env: process.env,
cwd: workspaceRoot(),
};

// Copied from Makefile launcher:
// https://github.com/microsoft/vscode-makefile-tools/blob/36a51746d263b6fc4a9054924c388d2c8a49ee1b/src/launch.ts#L445
if (process.platform === 'win32') {
terminalOptions.shellPath = 'C:\\Windows\\System32\\cmd.exe';
}

const terminal = vscode.window.createTerminal(terminalOptions);
this.terminals.add(terminal);

terminal.sendText(`${command} ${args.join(' ')}`);
terminal.show();

return terminal;
}

public dispose() {
this.terminals.forEach((terminal) => terminal.dispose());
this.terminals.clear();
this.onTerminalClose.dispose();
}
}

export const getLauncher = () => {
if (!LAUNCHER) {
LAUNCHER = new Launcher();
}
return LAUNCHER;
};
57 changes: 55 additions & 2 deletions src/logger.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,35 @@
import * as vscode from 'vscode';

import { EXTENSION_NAME, SETTINGS } from './const';

enum LogLevel {
INFO = 'info',
WARNING = 'warning',
ERROR = 'error',
NONE = 'none',
}

export default class Logger {
let LOGGER: Logger;

class Logger implements vscode.Disposable {
private outputChannel: vscode.OutputChannel;
private level: LogLevel;
private onDidChangeConfigurationDisposable: vscode.Disposable;

constructor(channelName: string) {
this.outputChannel = vscode.window.createOutputChannel(channelName);
this.level =
vscode.workspace.getConfiguration(EXTENSION_NAME).get(SETTINGS.logLevel) ??
LogLevel.INFO;
this.onDidChangeConfigurationDisposable = vscode.workspace.onDidChangeConfiguration(
(e) => {
if (e.affectsConfiguration(`${EXTENSION_NAME}.${SETTINGS.logLevel}`)) {
this.level =
vscode.workspace.getConfiguration(EXTENSION_NAME).get(SETTINGS.logLevel) ??
this.level;
}
},
);
}

public info(message: string) {
Expand All @@ -31,10 +50,44 @@ export default class Logger {

public dispose() {
this.outputChannel.dispose();
this.onDidChangeConfigurationDisposable.dispose();
}

private log(message: string, level: LogLevel = LogLevel.INFO) {
if (!this.shouldLog(level)) return;

const timestamp = new Date().toISOString();
this.outputChannel.append(`[${timestamp}] [${level}] ${message}`);
const logMessage = `[${timestamp}] [${level}] ${message}`;
if (message.endsWith('\n')) {
this.outputChannel.append(logMessage);
} else {
this.outputChannel.appendLine(logMessage);
}
}

private shouldLog(level: LogLevel): boolean {
return getLogLevelValue(level) >= getLogLevelValue(this.level);
}
}

export const getLogger = (): Logger => {
if (!LOGGER) {
LOGGER = new Logger(EXTENSION_NAME);
}
return LOGGER;
};

const getLogLevelValue = (level: string): number => {
switch (level) {
case LogLevel.INFO:
return 0;
case LogLevel.WARNING:
return 1;
case LogLevel.ERROR:
return 2;
case LogLevel.NONE:
return 3;
default:
return 0;
}
};
Loading

0 comments on commit 78d203f

Please sign in to comment.