Skip to content

Instantly share code, notes, and snippets.

@JoosepAlviste
Last active April 2, 2025 19:12
Show Gist options
  • Save JoosepAlviste/43e03d931db2d273f3a6ad21134b3806 to your computer and use it in GitHub Desktop.
Save JoosepAlviste/43e03d931db2d273f3a6ad21134b3806 to your computer and use it in GitHub Desktop.
Automatically add async to function declaration when typing "await"
---@param types string[] Will return the first node that matches one of these types
---@param node TSNode|nil
---@return TSNode|nil
local function find_node_ancestor(types, node)
if not node then
return nil
end
if vim.tbl_contains(types, node:type()) then
return node
end
local parent = node:parent()
return find_node_ancestor(types, parent)
end
---When typing "await" add "async" to the function declaration if the function
---isn't async already.
local function add_async()
-- This function should be executed when the user types "t" in insert mode,
-- but "t" is not inserted because it's the trigger.
vim.api.nvim_feedkeys('t', 'n', true)
local buffer = vim.fn.bufnr()
local text_before_cursor = vim.fn.getline('.'):sub(vim.fn.col '.' - 4, vim.fn.col '.' - 1)
if text_before_cursor ~= 'awai' then
return
end
-- ignore_injections = false makes this snippet work in filetypes where JS is injected
-- into other languages
local current_node = vim.treesitter.get_node { ignore_injections = false }
local function_node = find_node_ancestor(
{ 'arrow_function', 'function_declaration', 'function', 'method_definition' },
current_node
)
if not function_node then
return
end
local function_text = vim.treesitter.get_node_text(function_node, 0)
if vim.startswith(function_text, 'async') then
return
end
local start_row, start_col = function_node:start()
vim.api.nvim_buf_set_text(buffer, start_row, start_col, start_row, start_col, { 'async ' })
end
vim.keymap.set('i', 't', add_async, { buffer = true })
@martinvysnovsky
Copy link

Great plugin.
Additionally, you could include the method_definition ancestor so it will work also with classes:

 local function_node = find_node_ancestor(
    { 'arrow_function', 'function_declaration', 'function', 'method_definition' },
    current_node
  )

@JoosepAlviste
Copy link
Author

Great plugin. Additionally, you could include the method_definition ancestor so it will work also with classes:

 local function_node = find_node_ancestor(
    { 'arrow_function', 'function_declaration', 'function', 'method_definition' },
    current_node
  )

Looks great, thanks! Updated the Gist 🎉

@hjdarnel
Copy link

hjdarnel commented Apr 2, 2025

Hi @JoosepAlviste, thanks for the gist! Is there an easy way to identify if the cursor is currently in a comment when triggering the keymap t in insert mode? I'm not having any luck with my attempts, such as

-- 1: doesn't work, nodetype is `statement_block`
vim.keymap.set("i", "t", function()
  if vim.treesitter.get_node():type() == "comment" then
    return
  end

  add_async()
end, { buffer = true, desc = "trigger add_async" })

-- 2: also doesn't work, attempting to reparse the unknown tree before getting node
vim.keymap.set("i", "t", function()
  vim.treesitter.get_parser():parse()
  if vim.treesitter.get_node():type() == "comment" then
    return
  end

  add_async()
end, { buffer = true, desc = "trigger add_async" })

For example, leaving a comment mentioning "await". Typing within this comment will unnecessarily add async to bar()

class FooClass {
  bar() {
    // we don't need to await[CURSOR HERE]
    const baz = doThing();
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment