Skip to content

Commit b74766f

Browse files
authored
fix: improve usage support (#255)
1 parent 3c0865b commit b74766f

22 files changed

Lines changed: 2243 additions & 98 deletions

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,16 @@ The mise-vscode extension integrates mise's core functionality into VS Code, hel
6363
### [mise.toml Language Support](https://hverlin.github.io/mise-vscode/reference/misetoml-language-support/)
6464
- 📝 Syntax highlighting for `mise.toml` files (and [tera templates](https://mise.jdx.dev/templates.html))
6565
- 📚 Autocompletion for `mise.toml` files
66+
- 🧩 Autocompletion & hover for [task arguments](https://mise.jdx.dev/tasks/task-arguments.html)
67+
(`usage` field, `#USAGE`/`#MISE` comments in file tasks, `$usage_*` variables)
6668
- 🔗 Go to definition, find references for mise tasks
6769

6870
### [Task Management](https://hverlin.github.io/mise-vscode/reference/tasks/)
6971

7072
- 🔍 Automatic detection of [mise tasks](https://mise.jdx.dev/tasks/)
7173
- ⚡ Run tasks directly from, `mise.toml` files, file tasks, the command palette
72-
or the activity bar (arguments are supported)
74+
or the activity bar (task arguments are prompted for, with choices and
75+
default values from the [usage spec](https://mise.jdx.dev/tasks/task-arguments.html))
7376
- 📝 View task definitions
7477
- ➕ Create new toml & file tasks
7578
- ⚡ Autocompletion of task dependencies

docs/src/content/docs/reference/Tasks.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,22 @@ You have several ways to run a task with the extension:
3131
Click on the run button to run the task. If some option/arguments are required,
3232
you will be prompted to enter them.
3333

34+
### Task arguments
35+
36+
If the task declares arguments with the
37+
[`usage` field](https://mise.jdx.dev/tasks/task-arguments.html) (or `#USAGE`
38+
comments in file tasks), the extension prompts for them before running:
39+
40+
- arguments with `choices` are picked from a list
41+
- `default` values are pre-filled
42+
- the `help` text of each arg/flag is shown in the prompt
43+
- optional arguments can be skipped
44+
45+
See
46+
[mise.toml language support](/mise-vscode/reference/misetoml-language-support/#task-arguments-usage-spec)
47+
for the editor support (autocompletion, hover, syntax highlighting) of the
48+
usage spec.
49+
3450
### Using the mise activity bar
3551

3652
![task-run-activity-bar.png](../../../assets/task-run-activity-bar.png)
@@ -90,6 +106,11 @@ You can create a file task or a toml task directly from the activity bar
90106
Using the command palette: `cmd|ctrl+shift+p`, search for
91107
`Mise: Create File task` or `Mise: Create Toml Task`
92108

109+
New file tasks are created with a working example of
110+
[task arguments](https://mise.jdx.dev/tasks/task-arguments.html) (`#USAGE`
111+
flag, argument with a default value, custom completion, and the corresponding
112+
`$usage_*` variables in the script).
113+
93114
## Task dependencies
94115

95116
You can visualize the dependencies of a task by using the `Mise: Visualize Tasks Dependencies` command.

docs/src/content/docs/reference/mise.toml-language-support.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,55 @@ Tool names and versions are autocompleted in the `tools` section of a `mise.toml
4343

4444
![Screenshot showing autocompletion of tool version](./autocomplete-tool-version.png)
4545

46+
In `.tool-versions` files, backend prefixes (`core:`, `npm:`, `cargo:`, ...) are
47+
also autocompleted, and typing a prefix such as `core:` suggests the registry
48+
tools of that backend (e.g. `core:node`).
49+
4650
#### Autocompletion for tasks
4751

4852
Code completion is provided for `depends = ["task_name"]`, `depends_post = ["task_name"]`, `wait_for = ["task_name"]`.
4953

54+
### Task arguments (usage spec)
55+
56+
Task arguments are declared with the
57+
[`usage` field](https://mise.jdx.dev/tasks/task-arguments.html) of a task,
58+
using the [usage spec](https://usage.jdx.dev/spec/). The extension provides:
59+
60+
- **Syntax highlighting** of `usage = '''...'''` blocks (KDL)
61+
- **Context-aware autocompletion**: directives (`arg`, `flag`, `complete`) at
62+
the start of a line, the attributes valid for that directive after it
63+
(`help`, `default`, `choices` blocks, `count`, `negate`, ...), without
64+
repeating attributes that are already set
65+
- **Hover documentation** for directives and attributes
66+
- **`$usage_*` variable completion**: typing `$` in a multiline `run` block
67+
suggests the `usage_*` environment variables derived from the args and
68+
flags of that task
69+
70+
```toml
71+
[tasks.deploy]
72+
usage = '''
73+
arg "<environment>" help="Target environment" {
74+
choices "dev" "staging" "prod"
75+
}
76+
flag "-v --verbose" help="Enable verbose output"
77+
'''
78+
run = '''
79+
echo "Deploying to ${usage_environment?}"
80+
'''
81+
```
82+
83+
#### File tasks
84+
85+
The same support is available in shell
86+
[file tasks](https://mise.jdx.dev/tasks/file-tasks.html):
87+
88+
- `#USAGE` lines get usage spec syntax highlighting, autocompletion, and hover
89+
- `#MISE` lines are highlighted as TOML, with autocompletion and hover for the
90+
[task configuration keys](https://mise.jdx.dev/tasks/task-configuration.html)
91+
(`description`, `alias`, `depends`, `sources`, ...)
92+
- Typing `$` in the script body suggests the `usage_*` variables from the
93+
`#USAGE` lines
94+
5095
### Code lens features
5196

5297
This extension adds the following code lens features:

package.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,13 @@
126126
"scopeName": "source.mise.shell",
127127
"path": "./syntaxes/shell.json"
128128
},
129+
{
130+
"injectTo": [
131+
"source.shell"
132+
],
133+
"scopeName": "inline.mise-usage",
134+
"path": "./syntaxes/usage-shell.json"
135+
},
129136
{
130137
"injectTo": [
131138
"source.toml"
@@ -180,6 +187,13 @@
180187
"path": "./snippets/file-tasks-snippets.json"
181188
}
182189
],
190+
"configurationDefaults": {
191+
"[toml]": {
192+
"editor.quickSuggestions": {
193+
"strings": "on"
194+
}
195+
}
196+
},
183197
"configuration": {
184198
"title": "Mise",
185199
"properties": {

snippets/file-tasks-snippets.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,15 @@
1111
"#MISE depends=[${5:\"dependency\"}]",
1212
"$0"
1313
]
14+
},
15+
"mise File Task Usage Spec": {
16+
"prefix": "mise-task-usage",
17+
"scope": "shellscript",
18+
"description": "Define file task arguments with the usage spec",
19+
"body": [
20+
"#USAGE arg \"<${1:file}>\" help=\"${2:File to use}\"",
21+
"#USAGE flag \"-v --verbose\" help=\"Enable verbose output\"",
22+
"$0"
23+
]
1424
}
1525
}

snippets/toml-tasks-snippets.json

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,19 @@
117117
"$0"
118118
]
119119
},
120+
"tomlTaskUsage": {
121+
"prefix": "task_usage",
122+
"description": "Create a mise task with arguments defined with the usage spec",
123+
"body": [
124+
"[tasks.${1:my_task}]",
125+
"${2:description = '${3:Task description}'\n}usage = '''",
126+
"arg \"<${4:file}>\" help=\"${5:File to use}\"",
127+
"flag \"-v --verbose\" help=\"Enable verbose output\"",
128+
"'''",
129+
"run = 'echo \"\\${usage_$4?}\"'",
130+
"$0"
131+
]
132+
},
120133
"tomlTaskFull": {
121134
"prefix": "task_full",
122135
"description": "Create a mise task with all possible options",
@@ -131,10 +144,13 @@
131144
"sources = [${8:'src/**/*'}]",
132145
"outputs = [${9:'dist/**/*'}]",
133146
"shell = '${10:bash -c}'",
134-
"run = [",
135-
" \"echo 'running step 1...'\",",
136-
" \"echo 'running step 2...'\"",
137-
"]",
147+
"usage = '''",
148+
"arg \"<${11:file}>\" help=\"${12:File to use}\"",
149+
"'''",
150+
"run = '''",
151+
"echo 'running step 1...'",
152+
"echo \"running step 2 with \\${usage_${11}?}\"",
153+
"'''",
138154
"$0"
139155
]
140156
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env bash
2+
#USAGE flag "--force" help="Force the run"
3+
#USAGE f
4+
#MISE d
5+
echo "file task fixture for usage completions"
6+
echo $usage_

src/e2e-tests/fixtures/task-execution-workspace/mise.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,17 @@ run = "echo 'Hello from mise test task'"
44

55
[tasks.echo-hello]
66
run = "echo 'Hello, World!'"
7+
8+
[tasks.greet]
9+
description = "Greet someone"
10+
usage = '''
11+
arg "<name>" help="Name to greet" {
12+
choices "alice" "bob"
13+
}
14+
flag "--greeting <greeting>" help="Greeting to use" default="hello"
15+
flag "-l --loud" help="Shout the greeting"
16+
'''
17+
run = '''
18+
echo "${usage_greeting?} ${usage_name?}"
19+
echo $usage_
20+
'''

src/e2e-tests/task-execution.e2e.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ suite("Task Execution Test Suite", function () {
4141

4242
assert.deepEqual(
4343
tasks.map((t) => t.name),
44-
["echo-hello", "test-e2e"],
44+
["echo-hello", "greet", "test-e2e"],
4545
);
4646

4747
const echoTask = tasks.find((t) => t.name === "echo-hello");

src/e2e-tests/tool-versions/tool-versions.e2e.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,58 @@ suite("Tool Versions File Test Suite", function () {
7979
);
8080
});
8181

82+
test("completes backend prefixes in the first token of a line", async () => {
83+
const completions =
84+
await vscode.commands.executeCommand<vscode.CompletionList>(
85+
"vscode.executeCompletionItemProvider",
86+
document.uri,
87+
new vscode.Position(lineOf("nodejs 20.11.0"), 0),
88+
);
89+
90+
const labels = completions.items.map((item) =>
91+
typeof item.label === "string" ? item.label : item.label.label,
92+
);
93+
assert.ok(
94+
labels.includes("core:") && labels.includes("npm:"),
95+
`Expected backend prefixes (core:, npm:) in completions, got ${labels.length} items`,
96+
);
97+
});
98+
99+
test("completes registry tools after a backend prefix", async () => {
100+
const editor = await vscode.window.showTextDocument(document);
101+
const lastLine = document.lineCount - 1;
102+
await editor.edit((edit) => {
103+
edit.insert(
104+
new vscode.Position(lastLine, document.lineAt(lastLine).text.length),
105+
"\ncore:",
106+
);
107+
});
108+
109+
try {
110+
const backendLine = lineOf("core:");
111+
const completions =
112+
await vscode.commands.executeCommand<vscode.CompletionList>(
113+
"vscode.executeCompletionItemProvider",
114+
document.uri,
115+
new vscode.Position(backendLine, "core:".length),
116+
);
117+
118+
const labels = completions.items.map((item) =>
119+
typeof item.label === "string" ? item.label : item.label.label,
120+
);
121+
assert.ok(
122+
labels.includes("core:node"),
123+
`Expected "core:node" after the core: prefix, got: ${labels.slice(0, 20).join(", ")}`,
124+
);
125+
assert.ok(
126+
labels.every((label) => label.startsWith("core:")),
127+
`Only core: tools should be offered after the core: prefix, got: ${labels.slice(0, 20).join(", ")}`,
128+
);
129+
} finally {
130+
await vscode.commands.executeCommand("undo");
131+
}
132+
});
133+
82134
test("provides hover for declared tools", async () => {
83135
const shellcheckLine = lineOf("shellcheck 0.10.0");
84136
const hovers =

0 commit comments

Comments
 (0)