Skip to content

Commit

Permalink
Add support for custom keybindings with arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
millerdev committed Jan 11, 2021
1 parent 787d8eb commit 3129b9a
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 29 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
0.1.1 - tbd
0.2.0 - tbd

- Add support for custom keybindings with arguments.
- Limit `ag` result item length to 150 as well as the maximum number of results
to 200.
- Log errors to PyXT output channel in VS Code.
Expand Down
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,34 @@ Commands will often provide contextual argument hints as the first item in the
list of quick-pick suggestions. Accepting this suggestion will invoke the
command with default values as shown.

### Keybindings

Keybindings may be assigned to any PyXT command having a corresponding VS Code
command. These commands may also be invoked directly via the Command Palette.
Additionally keybindings may be added for custom commands.

#### Example keybindings.json entries

_Ctrl+Shift+A O_ to open command bar with text: `open /my/favorite/directory/`

```json
{
"key": "ctrl+shift+a o",
"command": "pyxt.command",
"args": {"text": "open /my/favorite/directory/"}
}
```

_Ctrl+Shift+A D_ to immediately execute the command: `ag 'hot search'`

```json
{
"key": "ctrl+shift+a g",
"command": "pyxt.command",
"args": {"text": "ag 'hot search'", "exec": true}
}
```

### Adding your own custom commands

Adding a new PyXT command is as simple as writing a Python module and loading
Expand Down
65 changes: 38 additions & 27 deletions client/commander.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ function subscribe(getClient, context) {

function registerCommand(id, getClient, context) {
const slug = id === "pyxt.command" ? "" : (id.slice(5) + " ")
const reg = vscode.commands.registerCommand(id, () => {
const reg = vscode.commands.registerCommand(id, args => {
const client = getClient()
client && command(client, slug)
client && command(client, slug, args)
})
context.subscriptions.push(reg)
}
Expand All @@ -24,9 +24,16 @@ function setHistory(value) {
history = value
}

async function command(client, cmd="") {
async function command(client, cmd="", args={}) {
const value = args.text || ""
let filePath
try {
const filePath = await commandInput(client, cmd)
if (args.exec) {
const result = await exec(client, "do_command", cmd + value)
filePath = await dispatch(result, client, cmd, value)
} else {
filePath = await commandInput(client, cmd, value)
}
if (filePath) {
await openFile(filePath)
}
Expand All @@ -52,33 +59,37 @@ async function commandInput(client, cmd="", value="", completions) {
input.value = value
const result = await getCommandResult(input, client)
input.hide()
if (!result) {
return
}
if (result.type === "items") {
if (result.filter_results) {
return filterResults(result, cmd + input.value)
}
if (result.clear_history && result.command) {
return clearHistory(result)
}
const value_ = result.value
? result.value.slice(cmd.length)
: input.value
return commandInput(client, cmd, value_, result)
return dispatch(result, client, cmd, input.value)
} finally {
input.dispose()
}
}

function dispatch(result, client, cmd, value) {
if (!result) {
return Promise.resolve()
}
if (result.type === "items") {
if (result.filter_results) {
return filterResults(result, cmd + value)
}
if (result.type === "success") {
return result.value
if (result.clear_history && result.command) {
return clearHistory(result)
}
let message = result.message
if (!message) {
message = "Unknown error"
console.log(message, result)
if (result.value) {
value = result.value.slice(cmd.length)
}
throw new Error(message)
} finally {
input.dispose()
return commandInput(client, cmd, value, result)
}
if (result.type === "success") {
return Promise.resolve(result.value)
}
let message = result.message
if (!message) {
message = "Unknown error"
console.log(message, result)
}
throw new Error(message)
}

async function getCommandResult(input, client) {
Expand Down
44 changes: 44 additions & 0 deletions client/test/commander.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,50 @@ suite('Commander', () => {
assert(!await result)
})

test("should load command with args from keybinding", async () => {
client = util.mockClient(
["get_completions", ["open "], {items: [
{label: "dir/", offset: 5},
{label: "file", filepath: "/file", offset: 5},
]}],
)
let input
const args = {text: "open "}
const result = commander.command(client, "", args)
input = await env.inputItemsChanged()
env.assertItems(input, ["dir/", "file"])

input.hide()
assert(!await result)
})

test("should execute command with args from keybinding", async () => {
client = util.mockClient(
// return null value to avoid openFile call, which is hard to test here
["do_command", ["open file"], {type: "success", value: null}]
)
const args = {text: "open file", exec: true}
await commander.command(client, "", args)
})

test("should execute command returning completions with args from keybinding", async () => {
client = util.mockClient(
[
"do_command", ["open dir/"],
{type: "items", items: [
{label: "file", offset: 5},
], value: "open dir/"},
]
)
let input
const args = {text: "open dir/", exec: true}
const result = commander.command(client, "", args)
input = await env.inputItemsChanged()
env.assertItems(input, ["file"])
input.hide()
assert(!await result)
})

test("should show completions on accept command", async () => {
client = util.mockClient(
["get_completions", [""], {items: [{label: "open", offset: 0}]}],
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"repository": "https://github.com/millerdev/pyxt",
"license": "See LICENSE file",
"homepage": "https://github.com/millerdev/pyxt",
"version": "0.1.1",
"version": "0.2.0",
"publisher": "millerdev",
"engines": {
"vscode": "^1.34.0"
Expand Down

0 comments on commit 3129b9a

Please sign in to comment.