Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typehinting luasnip #1117

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/luasnip.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
*luasnip.txt* For NVIM v0.8.0 Last change: 2024 January 17
*luasnip.txt* For NVIM v0.8.0 Last change: 2024 February 14

==============================================================================
Table of Contents *luasnip-table-of-contents*
Expand Down
9 changes: 9 additions & 0 deletions lua/luasnip/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,16 @@ end
-- declare here to use in set_config.
local c

---@class UserConfig
---@field ext_opts table<string, table>
---@field history boolean
---@field update_events string[]
---@field updateevents string[]

---@alias SetConfig fun(user_config: UserConfig)

c = {
---@type SetConfig
L3MON4D3 marked this conversation as resolved.
Show resolved Hide resolved
set_config = function(user_config)
user_config = user_config or {}
local conf = vim.deepcopy(conf_defaults)
Expand Down
175 changes: 164 additions & 11 deletions lua/luasnip/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ local luasnip_data_dir = vim.fn.stdpath("cache") .. "/luasnip"

local log = require("luasnip.util.log").new("main")

---@alias GetActiveSnip fun(): table|nil

---@type GetActiveSnip
local function get_active_snip()
local node = session.current_nodes[vim.api.nvim_get_current_buf()]
if not node then
Expand All @@ -39,6 +42,9 @@ local function match_snippet(line, type)
)
end

---@alias GetSnippets fun(ft: string|nil, opts: table|nil): table<string, Snippet[]>

---@type GetSnippets
-- ft:
-- * string: interpreted as filetype, return corresponding snippets.
-- * nil: return snippets for all filetypes:
Expand All @@ -56,6 +62,15 @@ local function get_snippets(ft, opts)
return snippet_collection.get_snippets(ft, opts.type or "snippets") or {}
end

---@class SnipInfo
---@field name string
---@field trigger string
---@field description string
---@field wordTrig boolean
---@field regTrig boolean

---@alias SnipInfoFunction fun(snip: Snippet): SnipInfo

local function default_snip_info(snip)
return {
name = snip.name,
Expand All @@ -66,6 +81,9 @@ local function default_snip_info(snip)
}
end

---@alias Available fun(snip_info: SnipInfoFunction|nil): SnipInfo[]

---@type Available
local function available(snip_info)
snip_info = snip_info or default_snip_info

Expand Down Expand Up @@ -166,6 +184,7 @@ local function jump(dir)
return false
end
end

local function jump_destination(dir)
-- dry run of jump (+no_move ofc.), only retrieves destination-node.
return safe_jump_current(dir, true, { active = {} })
Expand Down Expand Up @@ -225,6 +244,17 @@ local function _jump_into_default(snippet)
return util.no_region_check_wrap(snippet.jump_into, snippet, 1)
end

---@class Snippet
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, this gave me a few thoughts:

  • I think the annotations should only communicate official API, which for a snippet means relatively few fields, I think trigger, name, description, captures and env would be official, or are so commonly-used that they won't be changed, while stuff like id and wordTrig should not (yet) be relied upon.
    Also, there are a few interesting functions for snippet, see here
  • This is kind of a weird place to define Snippet, we should probably use a separate file for basic classes like that (see lua/luasnip/_types.lua or lua/luasnip/extras/_extra_types.lua for examples of this) (also, looking at them, it may make sense to namespace our classes 😅)
  • Not sure if you recall my little digression on snippet vs expandable vs addable in the discussion you opened, but snip_expand would take an expandable. But since this distinction exists in very few places, and is yet another thing newcomers would have to deal with, it's probably fine, especially for now, to just make most things Snippet

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(correction: id can also be official 😅 Just saw that we have get_id_snippet, and that'd be pretty stupid without some way to get the id)

---@field id integer
---@field name string
---@field trigger string
---@field trigger_expand unknown
---@field description string
---@field wordTrig boolean
---@field regTrig boolean
---@field captures table

---@alias SnipExpand fun(snippet: Snippet, opts: table|nil): Snippet
L3MON4D3 marked this conversation as resolved.
Show resolved Hide resolved
-- opts.clear_region: table, keys `from` and `to`, both (0,0)-indexed.
local function snip_expand(snippet, opts)
local snip = snippet:copy()
Expand Down Expand Up @@ -305,8 +335,11 @@ local function snip_expand(snippet, opts)
return snip
end

---@class ExpandOpts
---@field jump_into_func table

---Find a snippet matching the current cursor-position.
---@param opts table: may contain:
---@param opts ExpandOpts|nil: may contain:
--- - `jump_into_func`: passed through to `snip_expand`.
---@return boolean: whether a snippet was expanded.
local function expand(opts)
Expand Down Expand Up @@ -390,6 +423,9 @@ local function expand_or_jump()
return false
end

---@alias LspExpand fun(body: string, opts: table|nil): nil

---@type LspExpand
local function lsp_expand(body, opts)
-- expand snippet as-is.
snip_expand(
Expand Down Expand Up @@ -440,6 +476,9 @@ local function change_choice(val)
session.current_nodes[vim.api.nvim_get_current_buf()] = new_active
end

---@alias SetChoice fun(choice_indx: integer): nil

---@type SetChoice
local function set_choice(choice_indx)
local active_choice =
session.active_choice_nodes[vim.api.nvim_get_current_buf()]
Expand All @@ -457,11 +496,15 @@ local function set_choice(choice_indx)
session.current_nodes[vim.api.nvim_get_current_buf()] = new_active
end

---@alias GetCurrentChoices fun(): string[]

---@type GetCurrentChoices
local function get_current_choices()
local active_choice =
session.active_choice_nodes[vim.api.nvim_get_current_buf()]
assert(active_choice, "No active choiceNode")

---@type string[]
local choice_lines = {}

active_choice:update_static_all()
Expand Down Expand Up @@ -523,6 +566,9 @@ local function active_update_dependents()
end
end

---@alias StoreSnippetDocstrings fun(snippet_table: any): nil

---@type StoreSnippetDocstrings
local function store_snippet_docstrings(snippet_table)
-- ensure the directory exists.
-- 493 = 0755
Expand Down Expand Up @@ -557,6 +603,9 @@ local function store_snippet_docstrings(snippet_table)
vim.loop.fs_write(docstring_cache_fd, util.json_encode(docstrings))
end

---@alias LoadSnippetDocstrings fun(snippet_table: table): nil

---@type LoadSnippetDocstrings
local function load_snippet_docstrings(snippet_table)
-- ensure the directory exists.
-- 493 = 0755
Expand Down Expand Up @@ -613,6 +662,9 @@ local function unlink_current_if_deleted()
end
end

---@alias ExitOutOfRegion fun(node: table): nil
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think we'll also need a Node-type, first for arguments like this, and second to communicate API (which is currently described here)


---@type ExitOutOfRegion
local function exit_out_of_region(node)
-- if currently jumping via luasnip or no active node:
if session.jump_active or not node then
Expand Down Expand Up @@ -647,8 +699,7 @@ local function exit_out_of_region(node)
-- stylua: ignore
-- leave if curser before or behind snippet
if pos[1] < snip_begin_pos[1] or
pos[1] > snip_end_pos[1] then

pos[1] > snip_end_pos[1] then
-- make sure the snippet can safely be entered, since it may have to
-- be, in `refocus`.
if not snippet:extmarks_valid() then
Expand Down Expand Up @@ -677,12 +728,18 @@ local function exit_out_of_region(node)
end
end

---@alias FiletypeExtend fun(ft: string, extend_ft: string[]): nil

---@type FiletypeExtend
-- ft string, extend_ft table of strings.
local function filetype_extend(ft, extend_ft)
vim.list_extend(session.ft_redirect[ft], extend_ft)
session.ft_redirect[ft] = util.deduplicate(session.ft_redirect[ft])
end

---@alias FiletypeSet fun(ft: string, fts: string[]): nil

---@type FiletypeSet
-- ft string, fts table of strings.
local function filetype_set(ft, fts)
session.ft_redirect[ft] = util.deduplicate(fts)
Expand Down Expand Up @@ -711,14 +768,22 @@ local function setup_snip_env()

setfenv(2, combined_table)
end

---@alias GetSnipEnv fun(): table

---@type GetSnipEnv
local function get_snip_env()
return session.get_snip_env()
end

---@type GetIdSnippet
local function get_id_snippet(id)
return snippet_collection.get_id_snippet(id)
end

---@alias AddSnippets fun(ft: string, snippets: Snippet[], opts: table|nil): nil
L3MON4D3 marked this conversation as resolved.
Show resolved Hide resolved

---@type AddSnippets
local function add_snippets(ft, snippets, opts)
-- don't use yet, not available in some neovim-versions.
--
Expand Down Expand Up @@ -749,11 +814,17 @@ local function add_snippets(ft, snippets, opts)
end
end

---@alias CleanInvalidated fun(opts: table|nil): nil

---@type CleanInvalidated
local function clean_invalidated(opts)
opts = opts or {}
snippet_collection.clean_invalidated(opts)
end

---@alias ActivateNode fun(opts: table|nil): nil

---@type ActivateNode
local function activate_node(opts)
opts = opts or {}
local pos = opts.pos or util.get_cursor_0ind()
Expand Down Expand Up @@ -808,10 +879,46 @@ local function activate_node(opts)
session.current_nodes[vim.api.nvim_get_current_buf()] = node
end


---@alias SnippetFunction fun(context: any, nodes: any, opts: any): unknown
---@alias TextNodeFunction fun(static_text: string, opts: any): unknown
---@alias FunctionNode fun(func: function, args: any, opts: any): unknown
---@alias InsertNodeFunction fun(pos: string, static_text: any, opts: any): unknown
---@alias ChoiceNodeFunction fun(pos: any, choices: any, opts: any): unknown
---@alias DynamicNodeFunction fun(func: function, args: any, opts: any): unknown
---@alias RestoreNodeFunction fun(opts: any): unknown
---@alias ParentIndexer fun(indx: any): unknown
---@alias IndentSnippetNode fun(pos: any, nodes: any, indent_text: string, opts: any): unknown
---@alias MultiSnippet fun(contexts: any, nodes: any, opts: any): unknown

---@class LazyDefs
---@field s SnippetFunction
---@field sn SnippetFunction
---@field t TextNodeFunction
---@field f FunctionNode
---@field i InsertNodeFunction
---@field c ChoiceNodeFunction
---@field d DynamicNodeFunction
---@field r RestoreNodeFunction
---@field snippet SnippetFunction
---@field snippet_node SnippetFunction
---@field parent_indexer ParentIndexer
---@field indent_snippet_node IndentSnippetNode
---@field text_node TextNodeFunction
---@field function_node FunctionNode
---@field insert_node InsertNodeFunction
---@field choice_node ChoiceNodeFunction
---@field dynamic_node DynamicNodeFunction
---@field restore_node RestoreNodeFunction
---@field parser Parser
---@field config UserConfig
---@field multi_snippet MultiSnippet
---@field snippet_source SnippetSource
---@field select_keys string
-- make these lazy, such that we don't have to load them before it's really
-- necessary (drives up cost of initial load, otherwise).
-- stylua: ignore
local ls_lazy = {
local lazy_defs = {
s = function() return require("luasnip.nodes.snippet").S end,
sn = function() return require("luasnip.nodes.snippet").SN end,
t = function() return require("luasnip.nodes.textNode").T end,
Expand All @@ -830,14 +937,57 @@ local ls_lazy = {
choice_node = function() return require("luasnip.nodes.choiceNode").C end,
dynamic_node = function() return require("luasnip.nodes.dynamicNode").D end,
restore_node = function() return require("luasnip.nodes.restoreNode").R end,
parser = function() return require("luasnip.util.parser") end,
config = function() return require("luasnip.config") end,
multi_snippet = function() return require("luasnip.nodes.multiSnippet").new_multisnippet end,
snippet_source = function() return require("luasnip.session.snippet_collection.source") end,
select_keys = function() return require("luasnip.util.select").select_keys end
parser = function() return require("luasnip.util.parser") end, ---@diagnostic disable-line: assign-type-mismatch
config = function() return require("luasnip.config") end, ---@diagnostic disable-line: assign-type-mismatch
multi_snippet = function() return require("luasnip.nodes.multiSnippet").new_multisnippet end, ---@diagnostic disable-line: assign-type-mismatch
snippet_source = function() return require("luasnip.session.snippet_collection.source") end, ---@diagnostic disable-line: assign-type-mismatch
select_keys = function() return require("luasnip.util.select").select_keys end ---@diagnostic disable-line: assign-type-mismatch
}

ls = lazy_table({
---@class LazyT
---@field expand_or_jumpable fun(): boolean
---@field expand_or_locally_jumpable fun(): boolean
---@field locally_jumpable fun(dir: number): boolean
---@field jumpable fun(dir: number): boolean
---@field expandable fun(): boolean
---@field in_snippet fun(): boolean
---@field expand fun(): boolean
---@field snip_expand SnipExpand
---@field expand_repeat fun(): nil
---@field expand_auto fun(): nil
---@field expand_or_jump fun(): boolean
---@field jump fun(dir: number): boolean
---@field get_active_snip GetActiveSnip
---@field choice_active fun(): boolean
---@field change_choice fun(val: any): nil
---@field set_choice SetChoice
---@field get_current_choices GetCurrentChoices
---@field unlink_current fun(): nil
---@field lsp_expand LspExpand
---@field active_update_dependents fun(): nil
---@field available Available
---@field exit_out_of_region ExitOutOfRegion
---@field load_snippet_docstrings LoadSnippetDocstrings
---@field store_snippet_docstrings StoreSnippetDocstrings
---@field unlink_current_if_deleted fun(): nil
---@field filetype_extend FiletypeExtend
---@field filetype_set FiletypeSet
---@field add_snippets AddSnippets
---@field get_snippets GetSnippets
---@field get_id_snippet GetIdSnippet
---@field setup_snip_env fun(): nil
---@field get_snip_env GetSnipEnv
---@field clean_invalidated CleanInvalidated
---@field jump_destination fun(dir: integer): table|nil
---@field session LuasnipSession
---@field cleanup fun(): nil
---@field refresh_notify fun(ft: string): nil
---@field env_namespace EnvNamespace
---@field setup fun(user_config: UserConfig): nil
---@field extend_decorator ExtendDecorator
---@field log LuasnipLog
---@field activate_node ActivateNode
local lazy_t = {
expand_or_jumpable = expand_or_jumpable,
expand_or_locally_jumpable = expand_or_locally_jumpable,
locally_jumpable = locally_jumpable,
Expand Down Expand Up @@ -881,6 +1031,9 @@ ls = lazy_table({
extend_decorator = extend_decorator,
log = require("luasnip.util.log"),
activate_node = activate_node,
}, ls_lazy)
}

---@type LazyT|LazyDefs
ls = lazy_table(lazy_t, lazy_defs)

return ls
15 changes: 15 additions & 0 deletions lua/luasnip/session/init.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
---@class LuasnipSession
---@field active_choice_nodes table
---@field config table
---@field current_nodes table<integer, table>
---@field ft_redirect table
---@field get_snip_env fun(): table
---@field jump_active boolean
---@field last_expand_opts any
---@field last_expand_snip any
---@field latest_load_ft unknown
---@field loaded_fts table
---@field ns_id number
---@field snippet_roots table
---@field event_node any
-- used to store values like current nodes or the active node for autocommands.
local M = {}

Expand Down Expand Up @@ -45,6 +59,7 @@ M.config = require("luasnip.default_config")

M.loaded_fts = {}

---@return table
function M.get_snip_env()
return M.config.snip_env
end
Expand Down
Loading