APIs to define and patch commands and slash commands.
Registers a command. Calling it again with the same name merges the new fields into the existing definition; set a field to config.unset to remove it.
Available keys:
name: Name of the commandrun: Callback functioncontexts: AST node context in which this command should be availablepriority: Command priority (how high it appears in the list)key: Windows/Linux key binding (and mac, if not separately defined)mac: Mac-specific key bindinghide: Hide this command from the Command PaletterequireMode: rw or ro — only enable this command in a particular mode (read-write, or read-only)opensInput: set to true when the command opens a picker or another text input, so running it from a tap (an action button, say) on a phone or tablet brings up the on-screen keyboard right awayExample:
command.define {
name = "My custom command",
run = function()
editor.flashNotification "Triggered my custom command"
end
}
Equivalent to command.define. Kept for compatibility; use it to update the definition of previously defined commands (including built-in ones).
Example:
-- To assign a new key binding and command priority to a built-in command
command.update {
name = "Stats: Show",
key = "Ctrl-Shift-t",
priority = 100
}
-- To disable key bindings of an existing command
command.update {
name = "Navigate: Document Picker",
key = "",
mac = "",
}
-- To remove a field from an existing definition
command.define {
name = "My custom command",
priority = config.unset,
}
Defines a custom slash command. Like command.define, repeated calls with the same name merge.
Supported keys in the spec:
name: name of the command, what you type after /displayName: the label shown in the menu instead of name (e.g. "Heading 1" for h1)description: Description of the commandicon: a Feather icon name, an md-* editor icon, or inline <svg> markupaliases: more words that find the command when typed after /group: the menu section it's listed under (e.g. Basic blocks)priority: higher sorts earlier within its grouponlyContexts, exceptContexts: where it's offeredrun: The callback function that will be invoked once the command is run.Example:
slashCommand.define {
name = "hello-world",
run = function()
editor.insertAtCursor("Hello |^| world!", false, true)
end
}
Most of the heavy lifting happens in SB itself. These are just wrappers around the config object API.
-- priority: 99
command = command or {}
slashCommand = slashCommand or {}
-- Deprecated: use command.define.
slashcommand = slashCommand
--- Registers a command, or merges `def` into an existing command with the same name; set a field to `config.unset` to remove it.
--- @param def table { name: string, run: function, contexts?: string[], priority?: number, key?: string, mac?: string, hide?: boolean, requireMode?: "rw"|"ro", opensInput?: boolean }
--- @see API/command
function command.define(def)
config.merge({"commands", def.name}, def)
end
--- Equivalent to `command.define`; kept for compatibility, e.g. to change the key binding of a built-in command.
--- @param newDef table { name: string } plus any keys accepted by `command.define`
--- @see API/command
function command.update(newDef)
config.merge({"commands", newDef.name}, newDef)
end
--- Defines a slash command, or merges `def` into an existing one with the same name.
--- @param def table { name: string, run: function, displayName?: string, description?: string, icon?: string, aliases?: string[], group?: string, priority?: number, onlyContexts?: string[], exceptContexts?: string[] }
--- @see API/slashCommand
function slashCommand.define(def)
config.merge({"slashCommands", def.name}, def)
end
local function withCursor(text)
text = (string.gsub(text, "\n+$", ""))
if not string.find(text, "|^|", 1, true) then
text = text .. "|^|"
end
return text
end
-- A line break to end a block with, when the line after `line` has text that
-- would otherwise run into it
local function separatorAfter(line)
local doc = editor.getText()
if line.to >= #doc then
return ""
end
local nextLine = string.match(string.sub(doc, line.to + 2), "^[^\n]*")
return string.find(nextLine, "%S") and "\n" or ""
end
--- Inserts `text` as a block of its own where a slash command ran: in place of an empty line, else after the current line, with a blank line between. `|^|` in `text` places the cursor.
--- @param text string
--- @see API/slashCommand
function slashCommand.insertBlock(text)
text = withCursor(text)
local line = editor.getCurrentLine()
if not string.find(line.text, "%S") then
editor.replaceRange(line.from, line.to, text .. separatorAfter(line), true)
return
end
-- Without trailing whitespace (the space typed before the `/`)
local trimmed = string.gsub(line.text, "%s+$", "")
editor.replaceRange(line.from + #trimmed, line.to,
"\n\n" .. text .. separatorAfter(line), true)
end
--- Turns the current line into `text`: the line's text goes where `|^|` is (and the cursor after it). On an empty line, `text` is inserted as is.
--- @param text string
--- @see API/slashCommand
function slashCommand.convertLine(text)
text = withCursor(text)
local line = editor.getCurrentLine()
local body = string.match(line.text, "^%s*(.-)%s*$")
local at = string.find(text, "|^|", 1, true)
text = string.sub(text, 1, at - 1) .. body .. string.sub(text, at)
editor.replaceRange(line.from, line.to, text .. separatorAfter(line), true)
end