Each behavior is implemented in Lua using the following structure:
local M = {
--- Determines if the code-action can be used in the current selection.
-- @param lsp_range Range: The current selection or cursor position from the editor frontend via LSP.
-- @return boolean: True if the code-action can be used, otherwise false.
is_triggered = function(lsp_range)
return true
end,
--- Returns the name of the code-action.
-- @return string: The name of the code-action.
get_action_name = function()
return "Improve Wording"
end,
--- Post-processes any LLM reponse.
-- @param llm_response string: The answer from the LLM.
-- @param lsp_range Range: The current selection or cursor position from the editor frontend via LSP.
-- @return string: The post-processed LLM answer.
process_answer = function(llm_response, lsp_range)
return llm_response
end,
--- Creates a prompt string for the LLM.
-- @param lsp_range Range: The current selection or cursor position from the editor frontend via LSP.
-- @return string: The prompt string for the LLM.
create_prompt = function(lsp_range)
local selected_text = active_doc:text_from_range(lsp_range)
return table.concat({
[=====[
Human:
Improve the wording, fix grammar and typos. DO NOT output anything else. Just the improved text. No explanation.
]=====], selected_text, [=====[
Assistant: ]=====]
})
end,
--- Determines where to place the post-processed LLM answer.
-- @param lsp_range Range: The current selection or cursor position from the editor frontend via LSP.
-- @return Range: The range where the post-processed LLM answer should be placed.
get_placement_range = function(lsp_range)
return lsp_range
end
}
return M
It is recommended to inspect some existing code-actions for better understanding, such as those for Rust.
Behavior can also be configured in YAML. This approach is simpler but offers limited functionality compared to Lua. Below is an example to add a docstring to a Python function.
def add(a, b):
return a + b
def add(a, b):
"""Add two numbers.
Args:
a (int or float): The first number to add.
b (int or float): The second number to add.
Returns:
int or float: The sum of a and b.
Examples:
>>> add(2, 3)
5
>>> add(2.5, 3.2)
5.7
"""
return a + b
-
Specify the Trigger: Using the Tree-sitter Playground, determine that this code action should be enabled whenever the cursor is within a
function_definition
node.triggers: - kind: function_definition relation: findup
This configuration starts at the current node under the cursor and traverses the AST up until the first
function_definition
node is found. If no such node is found, the action is disabled. -
Form the Prompt: Provide context to the LLM by using the entire function. Context can be more comprehensive, such as using the entire source code.
context: kind: function_definition relation: findup # findup | exact hints: - name: FUNCTION_CONTEXT query: ((function_definition) @function)
This sets the start node to extract hints (like parameters and function body) to be interpolated into the prompt.
-
Define the Prompt Template: Use the extracted hints in the prompt template.
prompt_template: | Generate a comprehensive docstring for the following function: <<<FUNCTION_CONTEXT>>>
-
Post-process the Answer: Optionally post-process the LLM's answer using an
answer_template
.answer_template: "{<<ANSWER>>>}"
-
Determine the Placement: Specify where the processed answer should be placed using tree-sitter queries. For Python functions, try replacing the existing docstring first, then add a new docstring if none exists.
placement_strategies: # Try to find the docstring node - query: | (function_definition body: (block (expression_statement (string) @docstring))) position: replace_block # If not existent, find the body node and place it before - query: | (function_definition body: (block) @body) position: before
Refer to the config/code_actions/
directory for more examples.