Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .vscodeignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ eslint.config.mjs
justfile
tsconfig.json
webpack.config.js

# Explicitly include bin directory for bundled just-lsp binaries
!bin/**
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- Or operator `||` from `just` release 1.37.0
- F-string specifier from `just` release 1.44.0
- Leverage VSCode's built-in formatting API for `justfile` formatting
- Integrate `just-lsp` with extension

## [0.8.0] - 2025-01-02

Expand Down
12 changes: 2 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ Basic syntax highlighting for `just` files:
- Recipes: recipe attributes, names, params and dependencies
- Keywords, constants and operators
- Some embedded languages
- Integration with [just-lsp](https://github.com/terror/just-lsp)

Commands:

- Format on save
- Run recipe
- Task running


Demo:

- VSCode with `just` syntax highlighting
Expand Down Expand Up @@ -107,7 +107,6 @@ Outstanding:

- [ ] Improve handling of recipe body highlighting
- [ ] Improve handling of embedded languages
- [ ] Integrate [just-lsp](https://github.com/terror/just-lsp)
- [ ] Inline command runner
- [ ] Support code outline

Expand All @@ -121,14 +120,7 @@ Completed:
- [x] Format on save
- [x] Run recipe
- [x] Default formatter support

### Beyond

- Semantic highlighting / LSP

To avoid implementing a parser for files, it would be ideal for `just` to expose the AST or other APIs for editor extensions to leverage. This would allow for more advanced features like semantic highlighting, code folding, and more.

If VSCode works to support tree-sitter, [that](https://github.com/IndianBoy42/tree-sitter-just) would be a possible alternative.
- [x] Integrate [just-lsp](https://github.com/terror/just-lsp)

## Contributing

Expand Down
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@
"default": "just",
"description": "Path to just binary."
},
"vscode-just.lspPath": {
"type": "string",
"default": "just-lsp",
"description": "Path to just-lsp binary."
},
"vscode-just.runInTerminal": {
"type": "boolean",
"default": false,
Expand Down Expand Up @@ -146,6 +151,7 @@
},
"dependencies": {
"@types/yargs-parser": "^21.0.3",
"vscode-languageclient": "^9.0.1",
"yargs-parser": "^21.1.1"
}
}
1 change: 1 addition & 0 deletions src/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const COMMANDS = {
};
export const SETTINGS = {
justPath: 'justPath',
lspPath: 'lspPath',
runInTerminal: 'runInTerminal',
useSingleTerminal: 'useSingleTerminal',
logLevel: 'logLevel',
Expand Down
12 changes: 7 additions & 5 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { COMMANDS, EXTENSION_NAME } from './const';
import { formatJustfileTempFile } from './format';
import { getLauncher } from './launcher';
import { getLogger } from './logger';
import { createLanguageClient, stopLanguageClient } from './lsp';
import { runRecipeCommand } from './recipe';
import { TaskProvider } from './tasks';

Expand Down Expand Up @@ -37,21 +38,22 @@ export const activate = (context: vscode.ExtensionContext) => {
}),
);

const runRecipeDisposable = vscode.commands.registerCommand(
COMMANDS.runRecipe,
async () => {
context.subscriptions.push(
vscode.commands.registerCommand(COMMANDS.runRecipe, async () => {
runRecipeCommand();
},
}),
);
context.subscriptions.push(runRecipeDisposable);

context.subscriptions.push(
vscode.tasks.registerTaskProvider(EXTENSION_NAME, new TaskProvider()),
);

createLanguageClient();
};

export const deactivate = () => {
console.debug(`${EXTENSION_NAME} deactivated`);
getLogger().dispose();
getLauncher().dispose();
stopLanguageClient();
};
102 changes: 102 additions & 0 deletions src/lsp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { spawn } from 'child_process';
import * as vscode from 'vscode';
import {
LanguageClient,
LanguageClientOptions,
ServerOptions,
} from 'vscode-languageclient/node';

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

const LOGGER = getLogger();

let client: LanguageClient | undefined;

export const createLanguageClient = async (): Promise<LanguageClient | null> => {
const lspPath = getLspPath();

const isAvailable = await checkLspAvailability(lspPath);
if (!isAvailable) {
vscode.window
.showWarningMessage(
`Just LSP binary found but not working: ${lspPath}. Please check the installation or configure the path in settings.`,
'Install Instructions',
)
.then((selection) => {
if (selection === 'Install Instructions') {
vscode.env.openExternal(
vscode.Uri.parse('https://github.com/terror/just-lsp#installation'),
);
}
});

return null;
}

const serverOptions: ServerOptions = {
command: lspPath,
args: [],
};

const clientOptions: LanguageClientOptions = {
documentSelector: [{ scheme: 'file', language: 'just' }],
synchronize: {
fileEvents: vscode.workspace.createFileSystemWatcher(
'**/{justfile,Justfile,.justfile,*.just}',
),
},
};

client = new LanguageClient(
'just-lsp',
'Just Language Server',
serverOptions,
clientOptions,
);

try {
await client.start();
LOGGER.info(`Just LSP started successfully using: ${lspPath}`);
return client;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
LOGGER.error(`Failed to start Just LSP: ${message}`);
vscode.window.showErrorMessage(`Failed to start Just Language Server: ${message}`);
return null;
}
};

export const stopLanguageClient = async (): Promise<void> => {
if (client) {
await client.stop();
client = undefined;
}
};

const checkLspAvailability = (lspPath: string): Promise<boolean> => {
return new Promise((resolve) => {
const process = spawn(lspPath, ['--version'], { stdio: 'ignore' });

process.on('close', (code: number) => {
resolve(code === 0);
});
process.on('error', () => {
resolve(false);
});

setTimeout(() => {
process.kill();
resolve(false);
}, 5000);
});
};

const getLspPath = (): string => {
// TODO: support bundled LSP binary
return (
(vscode.workspace
.getConfiguration(EXTENSION_NAME)
.get(SETTINGS.lspPath) as string) || 'just-lsp'
);
};
65 changes: 61 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2351,7 +2351,7 @@ minimatch@^3.0.3, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2:
dependencies:
brace-expansion "^1.1.7"

minimatch@^5.0.1, minimatch@^5.1.6:
minimatch@^5.0.1, minimatch@^5.1.0, minimatch@^5.1.6:
version "5.1.6"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96"
integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
Expand Down Expand Up @@ -2870,6 +2870,11 @@ semver@^7.3.4, semver@^7.3.5, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4, semve
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==

semver@^7.3.7:
version "7.7.3"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946"
integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==

serialize-javascript@^6.0.1, serialize-javascript@^6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2"
Expand Down Expand Up @@ -2982,7 +2987,16 @@ stoppable@^1.1.0:
resolved "https://registry.yarnpkg.com/stoppable/-/stoppable-1.1.0.tgz#32da568e83ea488b08e4d7ea2c3bcc9d75015d5b"
integrity sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==

"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"

string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
Expand Down Expand Up @@ -3023,7 +3037,14 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"

"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"

strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
Expand Down Expand Up @@ -3273,6 +3294,33 @@ v8-to-istanbul@^9.0.0:
"@types/istanbul-lib-coverage" "^2.0.1"
convert-source-map "^2.0.0"

vscode-jsonrpc@8.2.0:
version "8.2.0"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz#f43dfa35fb51e763d17cd94dcca0c9458f35abf9"
integrity sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==

vscode-languageclient@^9.0.1:
version "9.0.1"
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz#cdfe20267726c8d4db839dc1e9d1816e1296e854"
integrity sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==
dependencies:
minimatch "^5.1.0"
semver "^7.3.7"
vscode-languageserver-protocol "3.17.5"

vscode-languageserver-protocol@3.17.5:
version "3.17.5"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz#864a8b8f390835572f4e13bd9f8313d0e3ac4bea"
integrity sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==
dependencies:
vscode-jsonrpc "8.2.0"
vscode-languageserver-types "3.17.5"

vscode-languageserver-types@3.17.5:
version "3.17.5"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz#3273676f0cf2eab40b3f44d085acbb7f08a39d8a"
integrity sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==

vscode-oniguruma@^1.5.1:
version "1.7.0"
resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz#439bfad8fe71abd7798338d1cd3dc53a8beea94b"
Expand Down Expand Up @@ -3400,7 +3448,16 @@ workerpool@^6.5.1:
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544"
integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==

"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
Expand Down