Skip to content

Commit 30e2830

Browse files
authored
Add support for user-defined custom binary and folder linked extensions (#185)
1 parent 78dedfe commit 30e2830

7 files changed

Lines changed: 331 additions & 7 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ directly from your editor.
2323

2424
It can automatically
2525
[configure other extensions](https://hverlin.github.io/mise-vscode/reference/supported-extensions/)
26-
to use tools provided by `mise` in your current project.
26+
to use tools provided by `mise` in your current project, including support for custom extensions.
2727

2828
[![mise-vscode screenshot](screenshots/mise-vscode-screenshot.png)](https://hverlin.github.io/mise-vscode/)
2929

docs/src/content/docs/reference/Settings.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,3 +259,41 @@ Auto-detect mise bin path on startup.
259259

260260
---
261261

262+
##### `mise.customBinaryExtensions`
263+
- **Type:** `array` (array of `object`)
264+
- **Default:** `[]`
265+
266+
Custom binary extensions to automatically configure VSCode extensions with mise tool binaries.
267+
268+
Each entry requires:
269+
- `extensionId`: VSCode extension ID
270+
- `toolSources`: Array of mise tool registry sources to match
271+
- `vscodeSetting.key`: VSCode setting key to write
272+
273+
Optional:
274+
- `binName`: Binary name (defaults to first tool name)
275+
- `vscodeSetting.subdirs`: Subdirectories to append to the path
276+
- `supportsShims`: Whether extension supports shims (default: true)
277+
- `supportsSymlinks`: Whether extension supports symlinks (default: true)
278+
279+
---
280+
281+
##### `mise.customFolderExtensions`
282+
- **Type:** `array` (array of `object`)
283+
- **Default:** `[]`
284+
285+
Custom folder extensions to automatically configure VSCode extensions with mise tool folders.
286+
287+
Each entry requires:
288+
- `extensionId`: VSCode extension ID
289+
- `toolSources`: Array of mise tool registry sources to match
290+
- `vscodeSetting.key`: VSCode setting key to write
291+
- `folderName`: Name for the symlink folder
292+
293+
Optional:
294+
- `vscodeSetting.subdirs`: Subdirectories to append to the path written to VSCode setting
295+
- `sourceSubdirs`: Additional subdirs to find the tool source folder
296+
- `supportsSymlinks`: Whether extension supports symlinks (default: true)
297+
298+
---
299+

docs/src/content/docs/reference/Supported-extensions.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,44 @@ Extensions which have built-in support for `mise`:
4444
- [Elixir](https://marketplace.visualstudio.com/items?itemName=JakeBecker.elixir-ls)
4545
will look for `mise` and install the lsp server automatically
4646

47+
## Custom Extensions
48+
49+
You can configure any VSCode extension to use mise tools via settings:
50+
51+
- **`mise.customBinaryExtensions`**: Configure extensions that need a binary path
52+
- **`mise.customFolderExtensions`**: Configure extensions that need a folder path (e.g., JDK home)
53+
54+
Example for binary mode:
55+
56+
```json
57+
{
58+
"mise.customBinaryExtensions": [
59+
{
60+
"extensionId": "example.my-extension",
61+
"toolSources": ["aqua:mytool"],
62+
"vscodeSetting": { "key": "myExtension.toolPath" }
63+
}
64+
]
65+
}
66+
```
67+
68+
Example for folder mode:
69+
70+
```json
71+
{
72+
"mise.customFolderExtensions": [
73+
{
74+
"extensionId": "example.java-extension",
75+
"toolSources": ["jfox:java", "asdf:openjdk"],
76+
"vscodeSetting": { "key": "java.jdkHome" },
77+
"folderName": "custom-jdk"
78+
}
79+
]
80+
}
81+
```
82+
83+
See the Extension Settings for full configuration options.
84+
4785
Extensions that are not supported:
4886
- [Rust](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) does not offer to update the cargo path automatically. See [this discussion](https://github.com/hverlin/mise-vscode/discussions/70) for workarounds.
4987

package.json

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,100 @@
367367
"title": "Auto-detect mise bin path",
368368
"default": true,
369369
"markdownDescription": "Auto-detect mise bin path on startup."
370+
},
371+
"mise.customBinaryExtensions": {
372+
"order": 19,
373+
"type": "array",
374+
"default": [],
375+
"markdownDescription": "Custom binary extensions to automatically configure VSCode extensions with mise tool binaries.\n\nEach entry requires:\n- `extensionId`: VSCode extension ID\n- `toolSources`: Array of mise tool registry sources to match\n- `vscodeSetting.key`: VSCode setting key to write\n\nOptional:\n- `binName`: Binary name (defaults to first tool name)\n- `vscodeSetting.subdirs`: Subdirectories to append to the path\n- `supportsShims`: Whether extension supports shims (default: true)\n- `supportsSymlinks`: Whether extension supports symlinks (default: true)",
376+
"items": {
377+
"type": "object",
378+
"required": ["extensionId", "toolSources", "vscodeSetting"],
379+
"properties": {
380+
"extensionId": {
381+
"type": "string",
382+
"description": "VSCode extension ID (e.g., 'ms-python.python')"
383+
},
384+
"toolSources": {
385+
"type": "array",
386+
"items": { "type": "string" },
387+
"description": "Mise tool registry sources to find the tool (e.g., ['python'], ['node', 'aqua:bufbuild/buf'])"
388+
},
389+
"vscodeSetting": {
390+
"type": "object",
391+
"required": ["key"],
392+
"properties": {
393+
"key": { "type": "string", "description": "VSCode setting key (e.g., 'python.defaultInterpreterPath')" },
394+
"subdirs": {
395+
"type": "array",
396+
"items": { "type": "string" },
397+
"description": "Subdirectories to append to the tool path (e.g., ['bin'] to point to bin folder)"
398+
}
399+
}
400+
},
401+
"binName": {
402+
"type": "string",
403+
"description": "Binary name to look up (defaults to first tool name)"
404+
},
405+
"supportsShims": {
406+
"type": "boolean",
407+
"default": true,
408+
"description": "Whether this extension supports mise shims. If false, shims will not be used."
409+
},
410+
"supportsSymlinks": {
411+
"type": "boolean",
412+
"default": true,
413+
"description": "Whether this extension supports symlinks. If false, symlinks will not be used."
414+
}
415+
}
416+
}
417+
},
418+
"mise.customFolderExtensions": {
419+
"order": 20,
420+
"type": "array",
421+
"default": [],
422+
"markdownDescription": "Custom folder extensions to automatically configure VSCode extensions with mise tool folders.\n\nEach entry requires:\n- `extensionId`: VSCode extension ID\n- `toolSources`: Array of mise tool registry sources to match\n- `vscodeSetting.key`: VSCode setting key to write\n- `folderName`: Name for the symlink folder\n\nOptional:\n- `vscodeSetting.subdirs`: Subdirectories to append to the path written to VSCode setting\n- `sourceSubdirs`: Additional subdirs to find the tool source folder\n- `supportsSymlinks`: Whether extension supports symlinks (default: true)",
423+
"items": {
424+
"type": "object",
425+
"required": ["extensionId", "toolSources", "vscodeSetting", "folderName"],
426+
"properties": {
427+
"extensionId": {
428+
"type": "string",
429+
"description": "VSCode extension ID (e.g., 'ms-python.python')"
430+
},
431+
"toolSources": {
432+
"type": "array",
433+
"items": { "type": "string" },
434+
"description": "Mise tool registry sources to find the tool (e.g., ['python'], ['node', 'aqua:bufbuild/buf'])"
435+
},
436+
"vscodeSetting": {
437+
"type": "object",
438+
"required": ["key"],
439+
"properties": {
440+
"key": { "type": "string", "description": "VSCode setting key (e.g., 'java.jdkHome')" },
441+
"subdirs": {
442+
"type": "array",
443+
"items": { "type": "string" },
444+
"description": "Subdirectories to append to the path (e.g., ['bin'] to point to bin subfolder)"
445+
}
446+
}
447+
},
448+
"folderName": {
449+
"type": "string",
450+
"description": "Custom folder name for symlink"
451+
},
452+
"sourceSubdirs": {
453+
"type": "array",
454+
"items": { "type": "string" },
455+
"description": "Additional subdirs to find the tool source folder (e.g., ['lib'] to use parent/lib as base)"
456+
},
457+
"supportsSymlinks": {
458+
"type": "boolean",
459+
"default": true,
460+
"description": "Whether extension supports symlinks. If false, direct path will be used."
461+
}
462+
}
463+
}
370464
}
371465
}
372466
},

src/configuration.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ export const CONFIGURATION_FLAGS = {
2828
commandTTLCacheSeconds: "commandTTLCacheSeconds",
2929
showNotificationIfMissingTools: "showNotificationIfMissingTools",
3030
autoDetectMiseBinPath: "autoDetectMiseBinPath",
31+
customBinaryExtensions: "customBinaryExtensions",
32+
customFolderExtensions: "customFolderExtensions",
3133
} as const;
3234

3335
const getExtensionConfig = () => {
@@ -190,6 +192,37 @@ export const shouldAutoDetectMiseBinPath = () => {
190192
return getConfOrElse(CONFIGURATION_FLAGS.autoDetectMiseBinPath, true);
191193
};
192194

195+
type VSCodeSettingSubdirs = {
196+
key: string;
197+
subdirs?: string[];
198+
};
199+
200+
type CustomBinaryExtensionConfig = {
201+
extensionId: string;
202+
toolSources: string[];
203+
vscodeSetting: VSCodeSettingSubdirs;
204+
binName?: string;
205+
supportsShims?: boolean;
206+
supportsSymlinks?: boolean;
207+
};
208+
209+
type CustomFolderExtensionConfig = {
210+
extensionId: string;
211+
toolSources: string[];
212+
vscodeSetting: VSCodeSettingSubdirs;
213+
folderName: string;
214+
sourceSubdirs?: string[];
215+
supportsSymlinks?: boolean;
216+
};
217+
218+
export const getCustomBinaryExtensions = (): CustomBinaryExtensionConfig[] => {
219+
return getConfOrElse(CONFIGURATION_FLAGS.customBinaryExtensions, []);
220+
};
221+
222+
export const getCustomFolderExtensions = (): CustomFolderExtensionConfig[] => {
223+
return getConfOrElse(CONFIGURATION_FLAGS.customFolderExtensions, []);
224+
};
225+
193226
export type VSCodeSettingValue =
194227
| string
195228
| number

src/providers/toolsProvider.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ import {
3131
import { logger } from "../utils/logger";
3232
import { findToolPosition } from "../utils/miseFileParser";
3333
import { getWebsiteForTool } from "../utils/miseUtilts";
34-
import { CONFIGURABLE_EXTENSIONS_BY_TOOL_NAME } from "../utils/supportedExtensions";
34+
import {
35+
CONFIGURABLE_EXTENSIONS_BY_TOOL_NAME,
36+
getAllConfigurableExtensions,
37+
} from "../utils/supportedExtensions";
3538

3639
type TreeItem = ToolsSourceItem | ToolItem;
3740

@@ -622,11 +625,20 @@ export function registerToolsCommands(
622625
const ignoreList = getIgnoreList();
623626
const includeList = getIncludeList();
624627

628+
const allExtensions = getAllConfigurableExtensions();
629+
630+
const extByToolName = new Map<string, typeof allExtensions>();
631+
for (const ext of allExtensions) {
632+
for (const toolName of ext.toolNames) {
633+
const existing = extByToolName.get(toolName) ?? [];
634+
existing.push(ext);
635+
extByToolName.set(toolName, existing);
636+
}
637+
}
638+
625639
const tools = await miseService.getCurrentTools();
626640
const configurableTools = tools.filter((tool) => {
627-
const configurableExtensions = CONFIGURABLE_EXTENSIONS_BY_TOOL_NAME.get(
628-
tool.name,
629-
);
641+
const configurableExtensions = extByToolName.get(tool.name);
630642
if (!configurableExtensions?.length) {
631643
return false;
632644
}
@@ -659,8 +671,7 @@ export function registerToolsCommands(
659671
const configurableExtensionsWithTools = configurableTools
660672
.filter((tool) => tool.installed)
661673
.flatMap((tool) => {
662-
const configurableExtensions =
663-
CONFIGURABLE_EXTENSIONS_BY_TOOL_NAME.get(tool.name);
674+
const configurableExtensions = extByToolName.get(tool.name);
664675

665676
if (!configurableExtensions?.length) {
666677
return [];

0 commit comments

Comments
 (0)