├── .stylua.toml ├── README.md └── lua └── luasnip-snippets ├── config.lua ├── init.lua ├── nodes.lua ├── snippets ├── all.lua ├── cpp │ ├── commons.lua │ ├── default.lua │ ├── init.lua │ ├── lambda_fn.lua │ ├── postfix.lua │ ├── qt.lua │ ├── selection.lua │ └── statements.lua ├── dart │ ├── _treesitter.lua │ └── init.lua ├── lua │ ├── default.lua │ ├── init.lua │ └── vim.lua ├── nix.lua ├── rust │ ├── default.lua │ ├── impl_block.lua │ ├── init.lua │ ├── lambda_fn.lua │ ├── postfix.lua │ └── test_fn.lua └── typescript.lua └── utils ├── comment.lua ├── common_cond.lua ├── cond.lua ├── dummy_types.lua ├── init.lua ├── tbl.lua └── treesitter.lua /.stylua.toml: -------------------------------------------------------------------------------- 1 | indent_type = "Spaces" 2 | indent_width = 2 3 | column_width = 80 4 | call_parentheses = "NoSingleTable" 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

LuaSnip Snippets

2 | 3 |

4 | 5 | 6 | 7 | 8 | 9 | Repo Size 10 |

11 | 12 | A collection of snippets for various programming language. Some snippets are 13 | highly inspired by Jetbrain's IDEs. 14 | 15 | ## 📦 Installation 16 | 17 | ```lua 18 | { 19 | "TwIStOy/luasnip-snippets", 20 | dependencies = { "L3MON4D3/LuaSnip" }, 21 | event = { "InsertEnter" }, 22 | config = function() 23 | -- register all snippets into LuaSnip 24 | require("luasnip-snippets").setup() 25 | end 26 | } 27 | ``` 28 | 29 | `autosnippet` feature should be enabled in `LuaSnip`. 30 | 31 | ```lua 32 | local ls = require('luasnip') 33 | ls.setup({ 34 | enable_autosnippets = true, 35 | }) 36 | ``` 37 | 38 | # ⚙️ Configuration 39 | 40 | Config Example: 41 | 42 | ```lua 43 | ---@type LSSnippets.Config 44 | { 45 | user = { 46 | -- user's name, used in todo-related snippets now 47 | name = nil, 48 | }, 49 | snippet = { 50 | lua = { 51 | -- enable neovim related snippets in lua 52 | vim_snippet = false, 53 | }, 54 | cpp = { 55 | quick_type = { 56 | -- use `std::unordered_map` instead of `absl::flat_hash_map` 57 | extra_trig = { 58 | { trig = 'm', params = 2, template = 'std::unordered_map<%s, %s>' } 59 | }, 60 | -- enable qt-related snippets 61 | qt = true, 62 | -- whether to add cpplint related comments in some snippets 63 | cpplint = true, 64 | }, 65 | }, 66 | rust = { 67 | -- add `#[rstest]` to test function's attribute choices, if the test mod has already use `rstest` directly 68 | rstest_support = false, 69 | }, 70 | }, 71 | disable_auto_expansion = { 72 | -- disable these snippets' auto expansion 73 | cpp = { "i32", "i64" }, 74 | }, 75 | disable_langs = { 76 | -- disable these language's snippets 77 | -- "dart" 78 | } 79 | } 80 | ``` 81 | 82 | ## Snippets 83 | 84 |
85 | All 86 | 87 | #### Normal Snippets 88 | 89 | | Trig | Desc | 90 | | :-----: | ---------------------------------- | 91 | | `todo` | Expand to linewise `TODO` comment | 92 | | `fixme` | Expand to linewise `FIXME` comment | 93 | | `note` | Expand to linewise `NOTE` comment | 94 | 95 |
96 | 97 |
98 | Lua 99 | 100 | Snippets with `*` are available only when `vim_snippet` is enabled. 101 | 102 | #### Normal Snippets 103 | 104 | | Trig | Desc | Context Required | 105 | | :-----: | ------------------------------------------ | :--------------: | 106 | | `fn` | Expands to function definition. | No | 107 | | `req` | Expands to `require(...)` statement. | No | 108 | | `ifn`\* | Expand to `vim.F.if_nil(...)` expresstion. | No | 109 | 110 | #### Postfix Snippets 111 | 112 | ```scheme 113 | [ 114 | (function_call) 115 | (identifier) 116 | (expression_list) 117 | (dot_index_expression) 118 | (bracket_index_expression) 119 | ] @any_expr 120 | [ 121 | (dot_index_expression) 122 | (bracket_index_expression) 123 | ] @index_expr 124 | ``` 125 | 126 | | Trig | Desc (placehoder: `?`) | Expr before cursor | 127 | | :-------: | ----------------------------------------- | :----------------: | 128 | | `.ipairs` | Expands to `ipairs(?)` for-loop. | `any_expr` | 129 | | `.pairs` | Expands to `pairs(?)` for-loop. | `any_expr` | 130 | | `.isnil` | Expands to `if ? == nil then` statement. | `any_expr` | 131 | | `.tget`\* | Expands to `vim.tbl_get(...)` expression. | `index_expr` | 132 | 133 | #### Auto-snippets 134 | 135 | | Trig | Desc | Context Required | Could Disable AutoExpansion | 136 | | :--: | ------------------------------------------------------ | :--------------: | :-------------------------: | 137 | | `#i` | Expands to `require(...)` statement with type hinting. | No | No | 138 | 139 |
140 | 141 |
142 | Cpp 143 | 144 | #### Normal Snippets 145 | 146 | | Trig | Desc | Context Required | Qt | Support Selection | 147 | | :---------: | -------------------------------------------------------------------------------------------------- | :-----------------: | :-: | :---------------: | 148 | | `fn` | Expands to lambda function in argument list or function body, otherwise expand to normal function. | No | | Yes | 149 | | `\|trans` | Expands to ranges::views::transform pipe. | No | | | 150 | | `\|filter` | Expands to ranges::views::filter pipe. | No | | | 151 | | `cpo` | Expands to customize point object. | No | | | 152 | | `ns%s(%S+)` | Expands to namespace block (including comments). | No | | | 153 | | `itf` | Expands to a struct with default virtual destruction. | No | | | 154 | | `pvf` | Expands to a pure virtual function declaration. | In Class | | | 155 | | `qcls` | Expands to a class inherts from QObject. | No | Yes | | 156 | | `#if` | Wrap selected code in `#if ... #endif` block. | After cut selection | No | | 157 | | `if` | Wrap selected code in `if (...)` block. | After cut selection | No | | 158 | | `do` | Wrap selected code in `do ... while(0)` block. | After cut selection | No | | 159 | | `while` | Wrap selected code in `while (...)` block. | After cut selection | No | | 160 | | `#de` | Wrap selected code in `#define ...` block. | After cut selection | No | | 161 | 162 | #### Auto-snippets 163 | 164 | | Trig | Desc | Context Required | Could Disable AutoExpansion | Qt | Support Selection | 165 | | :------: | --------------------------------------------------------- | :---------------------------: | :-------------------------: | :-: | :---------------: | 166 | | `ctor!` | Expands to default constructor. | In Class | No | | | 167 | | `dtor!` | Expands to default destructor. | In Class | No | | | 168 | | `cc!` | Expands to default copy constructor. | In Class | No | | | 169 | | `mv!` | Expands to default move constructor. | In Class | No | | | 170 | | `ncc!` | Expands to delete copy constructor. | In Class | No | | | 171 | | `nmv!` | Expands to delete move constructor. | In Class | No | | | 172 | | `ncm!` | Expands to delete copy and move constructor. | In Class | No | | | 173 | | `once` | Expands to `pragma once` marker at the front of the file. | All lines before are comments | Yes | | | 174 | | `u8` | Expands to `uint8_t`. | No | Yes | | | 175 | | `u16` | Expands to `uint16_t`. | No | Yes | | | 176 | | `u32` | Expands to `uint32_t`. | No | Yes | | | 177 | | `u64` | Expands to `uint64_t`. | No | Yes | | | 178 | | `i8` | Expands to `int8_t`. | No | Yes | | | 179 | | `i16` | Expands to `int16_t`. | No | Yes | | | 180 | | `i32` | Expands to `int32_t`. | No | Yes | | | 181 | | `i64` | Expands to `int64_t`. | No | Yes | | | 182 | | `t(%s)!` | Evaluates (QET) marker, and expand to typename. | No | No | | | 183 | | `#"` | Expands to include statement with quotes. `#include ""`. | No | Yes | | | 184 | | `#<` | Expands to include statement with `<>`. `#include <>`. | No | Yes | | | 185 | | `#q` | Expands to include qt generated moc file. | No | Yes | Yes | | 186 | | `#?` | Expands to `ifdef ... endif` fragment. | No | Yes | | Yes | 187 | 188 | ##### Quick Expand Type markers 189 | 190 | | Marker | Expand Type | Parameter | 191 | | :----: | :-------------------- | :-------: | 192 | | `v` | `std::vector` | 1 | 193 | | `i` | `int32_t` | 0 | 194 | | `u` | `uint32_t` | 0 | 195 | | `s` | `std::string` | 0 | 196 | | `m` | `absl::flat_hash_map` | 2 | 197 | | `t` | `std::tuple` | `*` | 198 | 199 | Example: 200 | 201 | ``` 202 | tvi! -> std::vector 203 | tmss! -> absl::flat_hash_map 204 | ``` 205 | 206 | #### Postfix Snippets 207 | 208 | ```scheme 209 | [ 210 | (identifier) 211 | (field_identifier) 212 | ] @indent 213 | 214 | [ 215 | (call_expression) 216 | (identifier) 217 | (template_function) 218 | (subscript_expression) 219 | (field_expression) 220 | (user_defined_literal) 221 | ] @any_expr 222 | ``` 223 | 224 | | Trig | Desc (placehoder: `?`) | Expr before cursor | Selection Version | 225 | | :-------: | -------------------------------------------------------------------- | :----------------: | :---------------: | 226 | | `.be` | Expands to begin and end exprs. | `any_expr` | Yes | 227 | | `.cbe` | Expands to cbegin and cend exprs. | `any_expr` | Yes | 228 | | `.mv` | Wraps with `std::move(?)`. | `any_expr` | Yes | 229 | | `.fwd` | Wraps with `std::forward(?)`. | `any_expr` | Yes | 230 | | `.val` | Wraps with `std::declval()`. | `any_expr` | Yes | 231 | | `.dt` | Wraps with `decltype(?)`. | `any_expr` | Yes | 232 | | `.uu` | Wraps with `(void)?`. | `any_expr` | Yes | 233 | | `.ts` | Switches indent's coding style between `CamelCase` and `snake_case`. | `indent` | Yes | 234 | | `.sc` | Wraps with `static_cast<>(?)`. | `any_expr` | Yes | 235 | | `.rc` | Wraps with `reinterpret_cast<>(?)`. | `any_expr` | Yes | 236 | | `.single` | Wraps with `ranges::views::single(?)`. | `any_expr` | Yes | 237 | | `.await` | Expands to `co_await ?`. | `any_expr` | Yes | 238 | | `.in` | Expands to `if (...find)` statements. | `any_expr` | | 239 | 240 | #### Cpplint 241 | 242 | Currently, some snippets will be expanded with cpplint related comments, e.g. `once`(which will expand to `#pragma once // NOLINT(build/header_guard)`). 243 | 244 | You can control whether to add cpplint related comments in these snippets by: 245 | 246 | - Updating `snippet.cpp.cpplint` in your config. This will affect all buffers. 247 | - Setting buffer variable `b:LuasnipSnippetsCppCppLint`. This will only affect the current buffer, and it will override the global setting. 248 | 249 |
250 | 251 |
252 | Rust 253 | 254 | #### Normal Snippets 255 | 256 | | Trig | Desc | Context Required | 257 | | :---: | -------------------------------------------------------------------------------------------------------------------------------------------- | :--------------: | 258 | | `fn` | Expands to lambda function in argument list or function body, otherwise expand to normal function. | No | 259 | | `pc` | Expands to `pub(crate)`. | No | 260 | | `ps` | Expands to `pub(super)`. | No | 261 | | `ii` | Expands to `#[inline]`. | No | 262 | | `ia` | Expands to `#[inline(always)]`. | No | 263 | | `tfn` | Expands to a test function. `#[test]` or `#[tokio::test]` supported. With `snippet.rust.rstest_support` enabled, `#[rstest]` also supported. | No | 264 | | `pm` | Expands to a public method definition. | In impl block | 265 | 266 | #### Postfix Snippets 267 | 268 | ```scheme 269 | [ 270 | (struct_expression) 271 | (call_expression) 272 | (identifier) 273 | (field_expression) 274 | ] @expr 275 | 276 | [ 277 | (struct_expression) 278 | (call_expression) 279 | (identifier) 280 | (field_expression) 281 | 282 | (generic_type) 283 | (scoped_type_identifier) 284 | (reference_type) 285 | ] @expr_or_type 286 | ``` 287 | 288 | | Trig | Desc (placehoder: `?`) | Expr before cursor | 289 | | :--------: | ----------------------------------------------------------- | :----------------: | 290 | | `.rc` | Wraps with `Rc::new(?)` if expr, `Rc` if type. | `expr_or_type` | 291 | | `.arc` | Wraps with `Arc::new(?)` if expr, `Arc` if type. | `expr_or_type` | 292 | | `.box` | Wraps with `Box::new(?)` if expr, `Box` if type. | `expr_or_type` | 293 | | `.mu` | Wraps with `Mutex::new(?)` if expr, `Mutex` if type. | `expr_or_type` | 294 | | `.rw` | Wraps with `RwLock::new(?)` if expr, `RwLock` if type. | `expr_or_type` | 295 | | `.cell` | Wraps with `Cell::new(?)` if expr, `Cell` if type. | `expr_or_type` | 296 | | `.refcell` | Wraps with `RefCell::new(?)` if expr, `RefCell` if type. | `expr_or_type` | 297 | | `.ref` | Wraps with `&?`. | `expr_or_type` | 298 | | `.refm` | Wraps with `&mut ?`. | `expr_or_type` | 299 | | `.ok` | Wraps with `Ok(?)`. | `expr` | 300 | | `.err` | Wraps with `Err(?)`. | `expr` | 301 | | `.some` | Wraps with `Some(?)`. | `expr` | 302 | | `.println` | Wraps with `println!("{:?}", ?)`. | `expr` | 303 | | `.match` | Wraps with `match ? {}`. | `expr` | 304 | 305 |
306 | 307 |
308 | Dart 309 | 310 | #### Normal Snippets 311 | 312 | | Trig | Desc | Context Required | 313 | | :---: | ------------------------------------------------ | :--------------: | 314 | | `fn` | Expands to function definition. | No | 315 | | `wfn` | Expands to function definition returns a widget. | No | 316 | | `afn` | Expands to an async function definition. | No | 317 | 318 | #### Auto-snippets 319 | 320 | | Trig | Desc | Context Required | 321 | | :-----: | ----------------------------------------- | :--------------: | 322 | | `ctor!` | Expands to class constructor function. | In Class | 323 | | `js!` | Expands to json-related methods. | In Class | 324 | | `init!` | Expands to `initState` override function. | No | 325 | | `dis!` | Expands to `dispose` override function. | No | 326 | | `for!` | Expands to for-loop. | No | 327 | | `sfw!` | Expands to `StatefulWidget` class. | No | 328 | | `slw!` | Expands to `StatelessWidget` class. | No | 329 | 330 |
331 | 332 |
333 | Nix 334 | 335 | #### Normal Snippets 336 | 337 | | Trig | Desc | Context Required | 338 | | :-------: | -------------------------------- | :--------------: | 339 | | `@module` | Expands to a nix module declare. | No | 340 | 341 | #### Postfix Snippets 342 | 343 | ```scheme 344 | [ 345 | (identifier) 346 | ] @identifier 347 | [ 348 | ((binding 349 | expression: (_) @expr 350 | )) 351 | ] @binding 352 | ``` 353 | 354 | | Trig | Desc (placehoder: `?`) | Expr before cursor | 355 | | :------: | --------------------------------------- | :----------------: | 356 | | `.on` | Expands to enable option statement. | `identifier` | 357 | | `.split` | Expands bindings to full attrset style. | `binding` | 358 | 359 |
360 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/config.lua: -------------------------------------------------------------------------------- 1 | ---@class LSSnippets.Config.User 2 | ---@field name? string 3 | ---@field email? string 4 | 5 | ---@class LSSnippets.Config.Snippet.Lua 6 | ---@field vim_snippet? boolean 7 | ---@field cond? fun():boolean 8 | 9 | ---@class LSSnippets.Config.Snippet.Cpp.QuickType 10 | ---@field extra_trig? LSSnippets.Config.Snippet.Cpp.QuickType.Shortcut[] 11 | 12 | ---@class LSSnippets.Config.Snippet.Cpp.QuickType.Shortcut 13 | ---@field trig string One character trigger. Supports lowercase letters only. 14 | ---@field params number Number of template parameters. 15 | ---@field template string Template string. 16 | 17 | ---@class LSSnippets.Config.Snippet.Cpp 18 | ---@field quick_type? LSSnippets.Config.Snippet.Cpp.QuickType 19 | ---@field qt? boolean Enable Qt related snippets. 20 | ---@field cpplint? boolean Whether to add cpplint related comments in some snippets. 21 | 22 | ---@class LSSnippets.Config.Snippet.Rust 23 | ---@field rstest_support? boolean 24 | 25 | ---@alias LSSnippets.Config.Snippet.DisableSnippets string[] 26 | ---@alias LSSnippets.SupportLangs 'cpp'|'dart'|'lua'|'rust'|'nix'|'typescript'|'*' 27 | 28 | ---@class LSSnippets.Config.Snippet 29 | ---@field lua? LSSnippets.Config.Snippet.Lua 30 | ---@field cpp? LSSnippets.Config.Snippet.Cpp 31 | ---@field rust? LSSnippets.Config.Snippet.Rust 32 | 33 | ---@class LSSnippets.Config 34 | ---@field copyright_header? string 35 | ---@field user? LSSnippets.Config.User 36 | ---@field snippet? LSSnippets.Config.Snippet 37 | ---@field disable_auto_expansion? table 38 | ---@field disable_langs? LSSnippets.SupportLangs[] 39 | local config = {} 40 | 41 | ---@param opts? LSSnippets.Config 42 | local function setup(opts) 43 | opts = opts or {} 44 | config = vim.tbl_deep_extend("force", config, opts) 45 | end 46 | 47 | ---@return any 48 | local function get(key) 49 | local keys = vim.split(key, ".", { 50 | plain = true, 51 | trimempty = true, 52 | }) 53 | local value = config 54 | for _, k in ipairs(keys) do 55 | value = value[k] 56 | if value == nil then 57 | return nil 58 | end 59 | end 60 | return value 61 | end 62 | 63 | ---@generic T 64 | ---@param key string 65 | ---@param default T 66 | ---@return T 67 | local function get_default(key, default) 68 | local value = get(key) 69 | return value == nil and default or value 70 | end 71 | 72 | ---@return boolean 73 | local function auto_expansion_disabled(lang, trig) 74 | ---@type luasnip-snippets.utils.tbl 75 | local Tbl = require("luasnip-snippets.utils.tbl") 76 | local disabled_trigs = 77 | vim.F.if_nil(vim.tbl_get(config, "disable_auto_expansion", lang), {}) 78 | return Tbl.list_contains(disabled_trigs, trig) 79 | end 80 | 81 | ---@class luasnip-snippets.config 82 | local M = { 83 | setup = setup, 84 | get = get, 85 | get_default = get_default, 86 | auto_expansion_disabled = auto_expansion_disabled, 87 | } 88 | 89 | return M 90 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/init.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | ---@alias SnippetOrBuilder LuaSnip.Snippet|fun():SnippetOrBuilder 4 | 5 | ---Register snippets to luasnip. 6 | ---@param ft string filetype 7 | ---@param snippets SnippetOrBuilder[] 8 | local function add_snippets(ft, snippets) 9 | local ls = require("luasnip") 10 | local ret = {} 11 | for _, snippet in ipairs(snippets) do 12 | if type(snippet) == "function" then 13 | snippet = snippet() 14 | end 15 | ret[#ret + 1] = snippet 16 | end 17 | ls.add_snippets(ft, ret) 18 | end 19 | 20 | ---Load snippets from ft module. 21 | ---@param ft string 22 | ---@return SnippetOrBuilder[] 23 | local function load_snippets(ft) 24 | local snippets = require("luasnip-snippets.snippets." .. ft) 25 | if type(snippets) == "function" then 26 | snippets = snippets() 27 | end 28 | return snippets 29 | end 30 | 31 | ---Load and register snippets. 32 | ---@param fts string[] 33 | local function load_and_add_snippet(fts) 34 | for _, ft in ipairs(fts) do 35 | local snippets = load_snippets(ft) 36 | add_snippets(ft, snippets) 37 | end 38 | end 39 | 40 | ---@param opts? LSSnippets.Config 41 | function M.setup(opts) 42 | local Config = require("luasnip-snippets.config") 43 | Config.setup(opts) 44 | 45 | -- register snippets 46 | load_and_add_snippet(vim.tbl_filter(function(l) 47 | return not vim.tbl_contains(Config.get("disable_langs") or {}, l) 48 | end, { 49 | "cpp", 50 | "rust", 51 | "lua", 52 | "dart", 53 | "nix", 54 | "all", 55 | "typescript", 56 | })) 57 | end 58 | 59 | return M 60 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/nodes.lua: -------------------------------------------------------------------------------- 1 | ---@param idx number 2 | ---@param placeholder? string 3 | ---@param opts? table 4 | local function insert_node(idx, placeholder, opts) 5 | local ls = require("luasnip") 6 | if idx == 0 then 7 | return ls.insert_node(idx) 8 | end 9 | opts = opts or {} 10 | local extra_opts = { 11 | node_ext_opts = { 12 | active = { 13 | virt_text = { 14 | { 15 | " " .. idx .. ": " .. (opts.desc or placeholder or "insert"), 16 | "Comment", 17 | }, 18 | }, 19 | }, 20 | }, 21 | } 22 | opts = vim.tbl_extend("keep", opts, extra_opts) 23 | return ls.insert_node(idx, placeholder, opts) 24 | end 25 | 26 | ---@param idx number 27 | ---@param choices table 28 | ---@param opts? table 29 | local function choice_node(idx, choices, opts) 30 | local ls = require("luasnip") 31 | opts = opts or {} 32 | local extra_opts = { 33 | node_ext_opts = { 34 | active = { 35 | virt_text = { 36 | { " " .. idx .. ": " .. (opts.desc or "choice"), "Comment" }, 37 | }, 38 | }, 39 | }, 40 | } 41 | opts = vim.tbl_extend("keep", opts, extra_opts) 42 | return ls.choice_node(idx, choices, opts) 43 | end 44 | 45 | ---@alias LSSnippets.TrigMatcher fun(line_to_cursor: string, trigger: string): string, table 46 | ---@alias LSSnippets.CustomTrigEngine fun(trigger: string): LSSnippets.TrigMatcher 47 | ---@alias LSSnippets.TrigEngine "plain"|"pattern"|"ecma"|"vim"|LSSnippets.CustomTrigEngine 48 | 49 | ---@class LSSnippets.SnippetOptions 50 | ---@field [1] string Trigger 51 | ---@field name string? Snippet name 52 | ---@field dscr string? Snippet description 53 | ---@field mode string? Snippet mode, "w" for word trigger, "h" for hidden, "A" for autosnippet, "b" for line begin, "r" for regex pattern 54 | ---@field engine LSSnippets.TrigEngine? Snippet trigger engine. If "r" in mode, defaults to "pattern". 55 | ---@field hidden boolean? Hidden from completion, If "h" in mode, defaults to true. 56 | ---@field nodes LuaSnip.Node[] Expansion nodes 57 | ---@field priority number? Snippet priority 58 | ---@field lang string? Language of the snippet 59 | ---@field cond LSSnippets.ConditionObject? Condition object, including condition and show_condition 60 | ---@field resolveExpandParams nil|fun(snippet: LuaSnip.Snippet, line_to_cursor: string, matched_trigger: string, captures: table): table Function to decide whether the snippet can be expanded or not. 61 | ---@field opts table? Other options 62 | 63 | ---@param opts LSSnippets.SnippetOptions 64 | local function construct_snippet(opts) 65 | local CommonCond = require("luasnip-snippets.utils.common_cond") 66 | local ls = require("luasnip") 67 | ---@type luasnip-snippets.config 68 | local Config = require("luasnip-snippets.config") 69 | 70 | local trig = opts[1] 71 | local name = vim.F.if_nil(opts.name, trig) 72 | local dscr = vim.F.if_nil(opts.dscr, "Snippet: " .. name) 73 | local mode = vim.F.if_nil(opts.mode, "") 74 | local wordTrig = mode:match("w") ~= nil 75 | local trigEngine = vim.F.if_nil(opts.engine, "plain") 76 | if mode:match("r") ~= nil and opts.engine == nil then 77 | trigEngine = "pattern" 78 | end 79 | local hidden = vim.F.if_nil(opts.hidden, mode:match("h") ~= nil) 80 | local snippetType = mode:match("A") ~= nil and "autosnippet" or "snippet" 81 | if 82 | snippetType == "autosnippet" 83 | and opts.lang ~= nil 84 | and Config.auto_expansion_disabled(opts.lang, trig) 85 | then 86 | snippetType = "snippet" 87 | end 88 | local nodes = opts.nodes 89 | local priority = opts.priority or nil 90 | local cond = opts.cond or nil 91 | if mode:match("b") ~= nil then 92 | local line_begin = CommonCond.at_line_begin(trig) 93 | cond = cond and (cond + line_begin) or line_begin 94 | end 95 | local trig_arg = { 96 | trig = trig, 97 | name = name, 98 | dscr = dscr, 99 | wordTrig = wordTrig, 100 | trigEngine = trigEngine, 101 | hidden = hidden, 102 | priority = priority, 103 | snippetType = snippetType, 104 | condition = cond and cond.condition, 105 | show_condition = cond and cond.show_condition, 106 | resolveExpandParams = opts.resolveExpandParams, 107 | } 108 | return ls.s(trig_arg, nodes, opts.opts) 109 | end 110 | 111 | ---Construct a snippet for simple expansion. (word) -> (expand) 112 | ---@param word string 113 | ---@param expand string 114 | ---@param mode? string 115 | local function word_expand(word, expand, mode) 116 | local ls = require("luasnip") 117 | 118 | mode = mode or "w" 119 | if mode:match("w") == nil then 120 | mode = mode .. "w" 121 | end 122 | return construct_snippet { 123 | word, 124 | name = ("(%s) %s"):format(word, expand), 125 | dscr = ("Quickly expands %s to %s"):format(word, expand), 126 | mode = mode, 127 | nodes = ls.text_node(expand), 128 | } 129 | end 130 | 131 | ---@class luasnip-snippets.nodes 132 | local M = { 133 | insert_node = insert_node, 134 | choice_node = choice_node, 135 | construct_snippet = construct_snippet, 136 | word_expand = word_expand, 137 | } 138 | 139 | return M 140 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/snippets/all.lua: -------------------------------------------------------------------------------- 1 | ---@param keyword string 2 | local todo_comment = function(keyword) 3 | local ls = require("luasnip") 4 | local f = ls.function_node 5 | local snippet = require("luasnip-snippets.nodes").construct_snippet 6 | local Config = require("luasnip-snippets.config") 7 | 8 | return snippet { 9 | keyword, 10 | mode = "bw", 11 | nodes = { 12 | f(function() 13 | local CommentFt = require("luasnip-snippets.utils.comment") 14 | local ft = vim.api.nvim_get_option_value("filetype", { 15 | buf = 0, 16 | }) 17 | local pattern = CommentFt.get(ft, 1) 18 | local name = Config.get("user.name") 19 | if pattern == nil then 20 | -- keep the input 21 | pattern = "%s" 22 | end 23 | if type(pattern) == "table" then 24 | pattern = pattern[1] 25 | end 26 | local marker 27 | if name == nil then 28 | marker = (" %s: "):format(keyword:upper()) 29 | else 30 | marker = (" %s(%s): "):format(keyword:upper(), name) 31 | end 32 | return pattern:format(marker) 33 | end, {}), 34 | }, 35 | } 36 | end 37 | 38 | return { 39 | todo_comment("todo"), 40 | todo_comment("fixme"), 41 | todo_comment("note"), 42 | } 43 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/snippets/cpp/commons.lua: -------------------------------------------------------------------------------- 1 | local header_ext = { 2 | "hh", 3 | "h", 4 | "hpp", 5 | "hxx", 6 | "h++", 7 | "inl", 8 | "ipp", 9 | "tcc", 10 | } 11 | 12 | local function in_header_file() 13 | local ext = vim.fn.expand("%:e") 14 | if vim.list_contains(header_ext, ext) then 15 | return true 16 | end 17 | return false 18 | end 19 | 20 | ---@param lines string[] 21 | ---@return string[] 22 | local function fix_leading_whitespace(lines, indent) 23 | indent = vim.F.if_nil(indent, 2) 24 | local leading_whitespace = string.rep(" ", indent) 25 | local ret = {} 26 | local first = true 27 | for _, line in ipairs(lines) do 28 | if not first then 29 | table.insert(ret, leading_whitespace .. line) 30 | else 31 | first = false 32 | table.insert(ret, line) 33 | end 34 | end 35 | return ret 36 | end 37 | 38 | local function add_trailing_slash(lines) 39 | local ret = {} 40 | local max_len = 0 41 | for _, line in ipairs(lines) do 42 | max_len = math.max(max_len, #line) 43 | end 44 | for _, line in ipairs(lines) do 45 | local len = #line 46 | local diff = max_len - len 47 | table.insert(ret, line .. string.rep(" ", diff) .. " \\") 48 | end 49 | return ret 50 | end 51 | 52 | return { 53 | header_ext = header_ext, 54 | in_header_file = in_header_file, 55 | fix_leading_whitespace = fix_leading_whitespace, 56 | add_trailing_slash = add_trailing_slash, 57 | } 58 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/snippets/cpp/default.lua: -------------------------------------------------------------------------------- 1 | local ls = require("luasnip") 2 | ---@type luasnip-snippets.nodes 3 | local Nodes = require("luasnip-snippets.nodes") 4 | local snippet = Nodes.construct_snippet 5 | local i = Nodes.insert_node 6 | local c = Nodes.choice_node 7 | local fmta = require("luasnip.extras.fmt").fmta 8 | local f = ls.function_node 9 | local t = ls.text_node 10 | local rep = require("luasnip.extras").rep 11 | local CommonCond = require("luasnip-snippets.utils.common_cond") 12 | ---@type luasnip-snippets.config 13 | local Config = require("luasnip-snippets.config") 14 | ---@type luasnip-snippets.utils 15 | local Utils = require("luasnip-snippets.utils") 16 | 17 | local function cpo_snippet() 18 | local function cpo_func_to_namespace(name) 19 | -- try to convert name from pascal case to snake case 20 | if name:match("^[A-Z]") then 21 | -- is pascal case now, change to snake case 22 | name = name:gsub("(%u+)(%u%l)", "%1_%2") 23 | name = name:gsub("([a-z0-9])([A-Z])", "%1_%2") 24 | name = name:gsub("-", "_") 25 | name = name:lower() 26 | end 27 | return ("%s_fn"):format(name) 28 | end 29 | return snippet { 30 | "cpo", 31 | name = "(cpo) Customization point object", 32 | dscr = "Expands to a customization point object", 33 | mode = "bw", 34 | nodes = fmta( 35 | [[ 36 | namespace { 37 | struct Fn { 38 | template<> 39 | decltype(auto) operator()(T&& value) const noexcept(_noexcept) { 40 | 41 | } 42 | }; 43 | } // namespace 44 | inline constexpr ::Fn {}; 45 | ]], 46 | { 47 | name = i(1, "function name"), 48 | ns_name = f(function(args) 49 | return cpo_func_to_namespace(args[1][1]) 50 | end, { 1 }), 51 | cursor = i(0), 52 | } 53 | ), 54 | } 55 | end 56 | 57 | ---@param bits number 58 | ---@param unsigned boolean 59 | local function int_type_snippet(bits, unsigned) 60 | local prefix = unsigned and "u" or "" 61 | local trig = (unsigned and "u" or "i") .. bits 62 | local expand = ("%sint%s_t"):format(prefix, bits) 63 | return snippet { 64 | trig, 65 | name = ("(%s) %s"):format(trig, expand), 66 | desc = ("Expands to %s"):format(expand), 67 | mode = "wA", 68 | lang = "cpp", 69 | nodes = { 70 | t(expand), 71 | }, 72 | } 73 | end 74 | 75 | ---@param trig string 76 | ---@param func string 77 | local function ranges_views_snippet(trig, func) 78 | return snippet { 79 | trig, 80 | name = ("(%s) %s"):format(trig, func:gsub("^%l", string.upper)), 81 | dscr = ("Expands to %s view"):format(func), 82 | nodes = fmta( 83 | [[ 84 | | ::views::([&](auto&& value) { 85 | 86 | }) 87 | ]], 88 | { 89 | namespace = c(1, { 90 | t("ranges"), 91 | t("std"), 92 | }, { desc = "library" }), 93 | body = i(0), 94 | func = t(func), 95 | } 96 | ), 97 | } 98 | end 99 | 100 | local all_lines_before_are_all_comments = 101 | CommonCond.generate_all_lines_before_match_cond { 102 | "^%s*//.*$", 103 | "^%s*$", 104 | } 105 | 106 | local default_quick_markers = { 107 | v = { params = 1, template = "std::vector<%s>" }, 108 | i = { params = 0, template = "int32_t" }, 109 | s = { params = 0, template = "std::string" }, 110 | u = { params = 0, template = "uint32_t" }, 111 | m = { params = 2, template = "absl::flat_hash_map<%s, %s>" }, 112 | t = { params = -1, template = "std::tuple<%s>" }, 113 | } 114 | 115 | ---@param shortcut string 116 | ---@return string? 117 | local function quick_type(shortcut) 118 | ---@type luasnip-snippets.config 119 | local quick_markers = Config.get("snippet.cpp.quick_type.extra_trig") or {} 120 | local markers = vim.deepcopy(default_quick_markers) 121 | for _, marker in ipairs(quick_markers) do 122 | markers[marker.trig] = { 123 | params = marker.params, 124 | template = marker.template, 125 | } 126 | end 127 | 128 | ---@param s string 129 | ---@return string?, string? 130 | local function expect_typename(s) 131 | local first, rest = s:match("^(%l)(.*)$") 132 | if first == nil then 133 | return nil, nil 134 | end 135 | 136 | local trig = markers[first] 137 | if trig == nil then 138 | return nil, nil 139 | end 140 | 141 | if trig.params == -1 then 142 | local parameters = {} 143 | while #rest > 0 do 144 | local typename, sub_rest = expect_typename(rest) 145 | if typename == nil or sub_rest == nil then 146 | break 147 | end 148 | parameters[#parameters + 1] = typename 149 | rest = sub_rest 150 | end 151 | return (trig.template):format(table.concat(parameters, ", ")), rest 152 | end 153 | 154 | if trig.params == 0 then 155 | return trig.template, rest 156 | end 157 | 158 | local parameters = {} 159 | for _ = 1, trig.params do 160 | local typename, sub_rest = expect_typename(rest) 161 | if typename == nil or sub_rest == nil then 162 | return nil, rest 163 | end 164 | parameters[#parameters + 1] = typename 165 | rest = sub_rest 166 | end 167 | 168 | return string.format(trig.template, unpack(parameters)), rest 169 | end 170 | 171 | local result, rest = expect_typename(shortcut) 172 | if rest and #rest > 0 then 173 | print(("After QET eval, rest not empty: %s"):format(rest)) 174 | end 175 | if result == nil then 176 | return shortcut 177 | else 178 | return result 179 | end 180 | end 181 | 182 | return { 183 | cpo_snippet, 184 | 185 | ranges_views_snippet("|trans", "transform"), 186 | ranges_views_snippet("|filter", "filter"), 187 | 188 | -- progma once 189 | snippet { 190 | "once", 191 | name = "(once) #Progma once", 192 | dscr = "Expands to progma once with comments", 193 | mode = "bwA", 194 | lang = "cpp", 195 | cond = all_lines_before_are_all_comments, 196 | nodes = { 197 | f(function() 198 | local buffer_settings = 199 | Utils.get_buf_var(0, "LuasnipSnippetsCppCppLint") 200 | 201 | local use_cpplint = vim.F.if_nil( 202 | buffer_settings, 203 | Config.get_default("snippet.cpp.cpplint", true) 204 | ) 205 | if use_cpplint then 206 | return { "#pragma once // NOLINT(build/header_guard)", "" } 207 | else 208 | return { "#pragma once", "" } 209 | end 210 | end), 211 | }, 212 | }, 213 | 214 | -- include short cuts 215 | snippet { 216 | '#"', 217 | name = 'include ""', 218 | dscr = "#include with quotes", 219 | mode = "bA", 220 | lang = "cpp", 221 | nodes = { 222 | t('#include "'), 223 | i(1, "header"), 224 | t('"'), 225 | }, 226 | }, 227 | snippet { 228 | "#<", 229 | name = "include <>", 230 | dscr = "#include with <>", 231 | mode = "bA", 232 | lang = "cpp", 233 | nodes = { 234 | t("#include <"), 235 | i(1, "header"), 236 | t(">"), 237 | }, 238 | }, 239 | 240 | -- preprocessor directives short cuts 241 | snippet { 242 | "#?", 243 | name = "#ifdef", 244 | dscr = "Expands to #ifdef ... #endif", 245 | mode = "bA", 246 | lang = "cpp", 247 | nodes = fmta( 248 | [[ 249 | # 250 | 251 | #endif // 252 | ]], 253 | { 254 | directive = c(1, { 255 | t("ifdef"), 256 | t("ifndef"), 257 | }, { desc = "Directive" }), 258 | name = i(2, "name"), 259 | cursor = i(0), 260 | selected = f(function(_, snip) 261 | local _, env = {}, snip.env 262 | return env.LS_SELECT_RAW 263 | end), 264 | name_r = rep(2), 265 | } 266 | ), 267 | }, 268 | 269 | -- fast int types 270 | int_type_snippet(8, true), 271 | int_type_snippet(8, false), 272 | int_type_snippet(16, true), 273 | int_type_snippet(16, false), 274 | int_type_snippet(32, true), 275 | int_type_snippet(32, false), 276 | int_type_snippet(64, true), 277 | int_type_snippet(64, false), 278 | 279 | -- quick expand, expand stl types 280 | -- v = std::vector 281 | -- i = int32_t 282 | -- s = std::string 283 | -- u = uint32_t 284 | -- m = absl::flat_hash_map 285 | -- t = std::tuple 286 | ls.s({ 287 | trig = "t(%l+)!", 288 | wordTrig = true, 289 | regTrig = true, 290 | snippetType = "autosnippet", 291 | name = "(t) Quick types", 292 | desc = "Expands to a type", 293 | }, { 294 | f(function(_, snip) 295 | local shortcut = snip.captures[1] 296 | return quick_type(shortcut) 297 | end), 298 | }), 299 | 300 | snippet { 301 | "ns%s+(%S+)", 302 | name = "namespace", 303 | dscr = "namespace", 304 | mode = "br", 305 | nodes = fmta( 306 | [[ 307 | namespace { 308 | 309 | } // namespace 310 | ]], 311 | { 312 | body = i(0), 313 | name = f(function(_, snip) 314 | local parts = vim.split(snip.captures[1], "::", { 315 | plain = true, 316 | trimempty = true, 317 | }) 318 | local names = {} 319 | for _, part in ipairs(parts) do 320 | local nest_parts = vim.split(part, ".", { 321 | plain = true, 322 | trimempty = true, 323 | }) 324 | vim.list_extend(names, nest_parts) 325 | end 326 | return table.concat(names, "::") 327 | end), 328 | } 329 | ), 330 | }, 331 | } 332 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/snippets/cpp/init.lua: -------------------------------------------------------------------------------- 1 | local Utils = require("luasnip-snippets.utils") 2 | 3 | local function setup() 4 | local collections = { 5 | "statements", 6 | "lambda_fn", 7 | "postfix", 8 | "selection", 9 | "default", 10 | } 11 | 12 | local Config = require("luasnip-snippets.config") 13 | local qt_enabled = vim.F.if_nil(Config.get("snippet.cpp.qt"), true) 14 | 15 | if qt_enabled then 16 | collections[#collections + 1] = "qt" 17 | end 18 | 19 | return Utils.concat_snippets("luasnip-snippets.snippets.cpp", collections) 20 | end 21 | 22 | return setup 23 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/snippets/cpp/lambda_fn.lua: -------------------------------------------------------------------------------- 1 | local ls = require("luasnip") 2 | local UtilsTS = require("luasnip-snippets.utils.treesitter") 3 | local d = ls.dynamic_node 4 | local sn = ls.snippet_node 5 | local t = ls.text_node 6 | local fmta = require("luasnip.extras.fmt").fmta 7 | local CppCommons = require("luasnip-snippets.snippets.cpp.commons") 8 | local i = require("luasnip-snippets.nodes").insert_node 9 | local c = require("luasnip-snippets.nodes").choice_node 10 | local f = ls.function_node 11 | 12 | ---@class LSSnippets.Cpp.Fn.Env 13 | ---@field CPP_ARGUMENT_START { [1]: number, [2]: number }? 14 | ---@field CPP_FUNCTION_BODY_START { [1]: number, [2]: number }? 15 | ---@field CPP_CLASS_BODY_START { [1]: number, [2]: number }? 16 | ---@field CPP_IN_HEADER_FILE boolean 17 | ---@field CPP_IN_QUALIFIED_FUNCTION boolean 18 | ---@field LS_SELECT_RAW? string[] 19 | 20 | ---Returns if the node's declarator is qualified or not. 21 | ---@param node TSNode? `function_definition` node 22 | ---@return boolean 23 | local function is_qualified_function(node) 24 | if node == nil then 25 | return false 26 | end 27 | print(node:type()) 28 | assert(node:type() == "function_definition") 29 | local declarators = node:field("declarator") 30 | if declarators == nil or #declarators == 0 then 31 | return false 32 | end 33 | local declarator = declarators[1] 34 | print(declarator:type()) 35 | assert(declarator:type() == "function_declarator") 36 | declarators = declarator:field("declarator") 37 | if declarators == nil or #declarators == 0 then 38 | return false 39 | end 40 | declarator = declarators[1] 41 | print(declarator:type()) 42 | if declarator:type() == "qualified_identifier" then 43 | return true 44 | end 45 | return false 46 | end 47 | 48 | local function inject_expanding_environment(_, _, match, captures) 49 | local row, col = unpack(vim.api.nvim_win_get_cursor(0)) 50 | local buf = vim.api.nvim_get_current_buf() 51 | 52 | return UtilsTS.invoke_after_reparse_buffer(buf, match, function(parser, _) 53 | local pos = { 54 | row - 1, 55 | col - #match, 56 | } 57 | local node = parser:named_node_for_range { 58 | pos[1], 59 | pos[2], 60 | pos[1], 61 | pos[2], 62 | } 63 | 64 | local ret = { 65 | trigger = match, 66 | capture = captures, 67 | env_override = { 68 | CPP_ARGUMENT_START = UtilsTS.start_pos( 69 | UtilsTS.find_first_parent(node, { 70 | "argument_list", 71 | "parameter_list", 72 | }) 73 | ), 74 | CPP_FUNCTION_BODY_START = UtilsTS.start_pos( 75 | UtilsTS.find_first_parent(node, { 76 | "function_definition", 77 | "lambda_expression", 78 | "field_declaration", 79 | }) 80 | ), 81 | CPP_CLASS_BODY_START = UtilsTS.start_pos( 82 | UtilsTS.find_first_parent(node, { 83 | "struct_specifier", 84 | "class_specifier", 85 | }) 86 | ), 87 | CPP_IN_HEADER_FILE = CppCommons.in_header_file(), 88 | CPP_IN_QUALIFIED_FUNCTION = is_qualified_function( 89 | UtilsTS.find_first_parent(node, { 90 | "function_definition", 91 | }) 92 | ), 93 | }, 94 | } 95 | 96 | vim.api.nvim_win_set_cursor(0, { row, col }) 97 | return ret 98 | end) 99 | end 100 | 101 | ---@param env LSSnippets.Cpp.Fn.Env 102 | local function make_lambda_snippet_node(env) 103 | local captures = t("&") 104 | if env.CPP_CLASS_BODY_START or env.CPP_IN_QUALIFIED_FUNCTION then 105 | -- inside a member function 106 | captures = c(3, { 107 | t("this, &"), 108 | t("this"), 109 | t("&"), 110 | }) 111 | end 112 | 113 | local fmt_args = { 114 | captures = captures, 115 | body = i(0), 116 | specifier = c(1, { 117 | t(""), 118 | t(" mutable"), 119 | }), 120 | selected = f(function() 121 | return CppCommons.fix_leading_whitespace(env.LS_SELECT_RAW) 122 | end), 123 | args = i(2), 124 | } 125 | 126 | return sn( 127 | nil, 128 | fmta( 129 | [[ 130 | []() { 131 | 132 | } 133 | ]], 134 | fmt_args 135 | ) 136 | ) 137 | end 138 | 139 | ---@param env LSSnippets.Cpp.Fn.Env 140 | local function make_function_snippet_node(env) 141 | local fmt_args = { 142 | body = i(0), 143 | inline_inline = t(""), 144 | } 145 | local storage_specifiers = { 146 | t(""), 147 | t("static "), 148 | } 149 | if not env.CPP_IN_HEADER_FILE then 150 | storage_specifiers[#storage_specifiers + 1] = t("inline ") 151 | storage_specifiers[#storage_specifiers + 1] = t("static inline ") 152 | else 153 | fmt_args.inline_inline = t("inline ") 154 | end 155 | 156 | local specifiers = { 157 | t(""), 158 | t(" noexcept"), 159 | } 160 | if env.CPP_CLASS_BODY_START then 161 | specifiers[#specifiers + 1] = t(" const") 162 | specifiers[#specifiers + 1] = t(" const noexcept") 163 | end 164 | fmt_args.storage_specifier = 165 | c(1, storage_specifiers, { desc = "storage specifier" }) 166 | fmt_args.ret = i(2, "auto", { desc = "return type" }) 167 | fmt_args.name = i(3, "name", { desc = "function name" }) 168 | fmt_args.args = i(4, "args", { desc = "function arguments" }) 169 | fmt_args.specifier = c(5, specifiers, { desc = "specifier" }) 170 | fmt_args.selected = f(function() 171 | return CppCommons.fix_leading_whitespace(env.LS_SELECT_RAW) 172 | end) 173 | return sn( 174 | nil, 175 | fmta( 176 | [[ 177 | auto () ->> { 178 | 179 | } 180 | ]], 181 | fmt_args 182 | ) 183 | ) 184 | end 185 | 186 | return { 187 | ls.s( 188 | { 189 | trig = "fn", 190 | wordTrig = true, 191 | name = "(fn) Function-Definition/Lambda", 192 | resolveExpandParams = inject_expanding_environment, 193 | }, 194 | d(1, function(_, parent) 195 | local env = parent.env 196 | local last_type, last_type_row, last_type_col 197 | local keys = { 198 | "CPP_ARGUMENT_START", 199 | "CPP_FUNCTION_BODY_START", 200 | "CPP_CLASS_BODY_START", 201 | } 202 | for _, key in ipairs(keys) do 203 | if env[key] ~= nil then 204 | if last_type == nil then 205 | last_type = key 206 | last_type_row = env[key][1] 207 | last_type_col = env[key][2] 208 | else 209 | if 210 | last_type_row < env[key][1] 211 | or (last_type_row == env[key][1] and last_type_col < env[key][2]) 212 | then 213 | last_type = key 214 | last_type_row = env[key][1] 215 | last_type_col = env[key][2] 216 | end 217 | end 218 | end 219 | end 220 | 221 | if 222 | last_type == "CPP_ARGUMENT_START" 223 | or last_type == "CPP_FUNCTION_BODY_START" 224 | then 225 | return make_lambda_snippet_node(env) 226 | else 227 | return make_function_snippet_node(env) 228 | end 229 | end, {}) 230 | ), 231 | } 232 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/snippets/cpp/postfix.lua: -------------------------------------------------------------------------------- 1 | local ls = require("luasnip") 2 | local f = ls.function_node 3 | local tsp = require("luasnip.extras.treesitter_postfix") 4 | local Utils = require("luasnip-snippets.utils") 5 | local fmt = require("luasnip.extras.fmt").fmt 6 | local fmta = require("luasnip.extras.fmt").fmta 7 | local i = require("luasnip-snippets.nodes").insert_node 8 | ---@type luasnip-snippets.nodes 9 | local Nodes = require("luasnip-snippets.nodes") 10 | local snippet = Nodes.construct_snippet 11 | local CommonCond = require("luasnip-snippets.utils.common_cond") 12 | 13 | local expr_query = [[ 14 | [ 15 | (call_expression) 16 | (identifier) 17 | (template_function) 18 | (subscript_expression) 19 | (field_expression) 20 | (user_defined_literal) 21 | ] @prefix 22 | ]] 23 | 24 | local indent_query = [[ 25 | [ 26 | (identifier) 27 | (field_identifier) 28 | ] @prefix 29 | ]] 30 | 31 | ---@param trig string 32 | ---@param expand string 33 | ---@param dscr string? 34 | local function expr_tsp(trig, expand, dscr) 35 | local name = ("(%s) %s"):format(trig, expand) 36 | if dscr == nil then 37 | dscr = ("Wraps an expression with %s"):format(expand) 38 | else 39 | dscr = dscr:format(expand) 40 | end 41 | local replaced = expand:gsub("?", "%%s") 42 | 43 | return { 44 | snippet { 45 | trig, 46 | name = name, 47 | dscr = dscr, 48 | mode = "w", 49 | lang = "cpp", 50 | cond = CommonCond.has_select_raw, 51 | nodes = { 52 | f(function(_, snip) 53 | local _, env = {}, snip.env 54 | return Utils.replace_all(env.LS_SELECT_RAW, replaced) 55 | end), 56 | }, 57 | }, 58 | tsp.treesitter_postfix({ 59 | trig = "." .. trig, 60 | name = name, 61 | dscr = dscr, 62 | wordTrig = false, 63 | reparseBuffer = "live", 64 | matchTSNode = { 65 | query = expr_query, 66 | query_lang = "cpp", 67 | }, 68 | }, { 69 | f(function(_, parent) 70 | return Utils.replace_all(parent.snippet.env.LS_TSMATCH, replaced) 71 | end, {}), 72 | }), 73 | } 74 | end 75 | 76 | return { 77 | expr_tsp( 78 | "be", 79 | "?.begin(), ?.end()", 80 | "Completes an expr with both begin() and end()" 81 | ), 82 | expr_tsp( 83 | "cbe", 84 | "?.cbegin(), ?.cend()", 85 | "Completes an expr with both cbegin() and cend()" 86 | ), 87 | expr_tsp("mv", "std::move(?)"), 88 | expr_tsp("fwd", "std::forward(?)"), 89 | expr_tsp("val", "std::declval()"), 90 | expr_tsp("dt", "decltype(?)"), 91 | expr_tsp("uu", "(void)?"), 92 | expr_tsp("single", "ranges::views::single(?)"), 93 | expr_tsp("await", "co_await ?"), 94 | 95 | tsp.treesitter_postfix({ 96 | trig = ".ts", 97 | name = "(.ts) Toggle style", 98 | dscr = "Toggle previous indent's style", 99 | wordTrig = false, 100 | reparseBuffer = "live", 101 | matchTSNode = { 102 | query = indent_query, 103 | query_lang = "cpp", 104 | }, 105 | }, { 106 | f(function(_, parent) 107 | -- switch name style from snake to pascal or vice versa 108 | -- name must be a oneline identifier 109 | local name = table.concat(parent.snippet.env.LS_TSMATCH, "\n") 110 | if name:match("^[A-Z]") then 111 | -- is pascal case now, change to snake case 112 | name = name:gsub("(%u+)(%u%l)", "%1_%2") 113 | name = name:gsub("([a-z0-9])([A-Z])", "%1_%2") 114 | name = name:gsub("-", "_") 115 | return name:lower() 116 | else 117 | -- is snake case now, change to pascal case 118 | return name 119 | :gsub("_(%l)", function(s) 120 | return s:upper() 121 | end) 122 | :gsub("^%l", string.upper) 123 | :gsub("_$", "") 124 | end 125 | end, {}), 126 | }), 127 | 128 | tsp.treesitter_postfix( 129 | { 130 | trig = ".sc", 131 | name = "(.sc) static_cast(?)", 132 | dscr = "Wraps an expression with static_cast(?)", 133 | wordTrig = false, 134 | reparseBuffer = "live", 135 | matchTSNode = { 136 | query = expr_query, 137 | query_lang = "cpp", 138 | }, 139 | }, 140 | fmt( 141 | [[ 142 | static_cast<{body}>({expr}){end} 143 | ]], 144 | { 145 | body = i(1), 146 | expr = f(function(_, parent) 147 | return Utils.replace_all(parent.snippet.env.LS_TSMATCH, "%s") 148 | end, {}), 149 | ["end"] = i(0), 150 | } 151 | ) 152 | ), 153 | 154 | snippet { 155 | "sc", 156 | name = "(sc) static_cast(...)", 157 | dscr = "Wraps an expression with static_cast(...)", 158 | mode = "w", 159 | lang = "cpp", 160 | cond = CommonCond.has_select_raw, 161 | nodes = fmt( 162 | [[ 163 | static_cast<{body}>({selected}){cursor} 164 | ]], 165 | { 166 | cursor = i(0), 167 | body = i(1), 168 | selected = f(function(_, snip) 169 | local _, env = {}, snip.env 170 | return Utils.replace_all(env.LS_SELECT_RAW, "%s") 171 | end), 172 | } 173 | ), 174 | }, 175 | 176 | tsp.treesitter_postfix( 177 | { 178 | trig = ".rc", 179 | name = "(.rc) reinterpret_cast(?)", 180 | dscr = "Wraps an expression with reinterpret_cast(?)", 181 | wordTrig = false, 182 | reparseBuffer = "live", 183 | matchTSNode = { 184 | query = expr_query, 185 | query_lang = "cpp", 186 | }, 187 | }, 188 | fmt( 189 | [[ 190 | reinterpret_cast<{body}>({expr}){end} 191 | ]], 192 | { 193 | body = i(1), 194 | expr = f(function(_, parent) 195 | return Utils.replace_all(parent.snippet.env.LS_TSMATCH, "%s") 196 | end, {}), 197 | ["end"] = i(0), 198 | } 199 | ) 200 | ), 201 | 202 | snippet { 203 | "rc", 204 | name = "(rc) reinterpret_cast(...)", 205 | dscr = "Wraps an expression with reinterpret_cast(...)", 206 | mode = "w", 207 | lang = "cpp", 208 | cond = CommonCond.has_select_raw, 209 | nodes = fmt( 210 | [[ 211 | reinterpret_cast<{body}>({selected}){cursor} 212 | ]], 213 | { 214 | cursor = i(0), 215 | body = i(1), 216 | selected = f(function(_, snip) 217 | local _, env = {}, snip.env 218 | return Utils.replace_all(env.LS_SELECT_RAW, "%s") 219 | end), 220 | } 221 | ), 222 | }, 223 | 224 | tsp.treesitter_postfix( 225 | { 226 | trig = ".in", 227 | name = "(.in) if (...find)", 228 | dscr = "Expands to an if-expr to find an element in map-like object", 229 | wordTrig = false, 230 | reparseBuffer = "live", 231 | matchTSNode = { 232 | query = expr_query, 233 | query_lang = "cpp", 234 | }, 235 | }, 236 | fmta( 237 | [[ 238 | if (auto it = .find(); it != .end()) { 239 | 240 | } 241 | ]], 242 | { 243 | cursor = i(0), 244 | key = i(1, "Key"), 245 | expr = f(function(_, parent) 246 | return Utils.replace_all(parent.snippet.env.LS_TSMATCH, "%s") 247 | end, {}), 248 | } 249 | ) 250 | ), 251 | } 252 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/snippets/cpp/qt.lua: -------------------------------------------------------------------------------- 1 | local ls = require("luasnip") 2 | local UtilsTS = require("luasnip-snippets.utils.treesitter") 3 | local d = ls.dynamic_node 4 | local sn = ls.snippet_node 5 | local t = ls.text_node 6 | local fmta = require("luasnip.extras.fmt").fmta 7 | local snippet = require("luasnip-snippets.nodes").construct_snippet 8 | local i = require("luasnip-snippets.nodes").insert_node 9 | local rep = require("luasnip.extras").rep 10 | 11 | return { 12 | snippet { 13 | "qcls", 14 | name = "Q_OBJECT class", 15 | dscr = "Declare a class with Q_OBJECT macro", 16 | mode = "bw", 17 | nodes = fmta( 18 | [[ 19 | class <> : public QObject { 20 | Q_OBJECT 21 | 22 | public: 23 | ~<>() = default; 24 | }; 25 | ]], 26 | { 27 | i(1, "Class Name"), 28 | rep(1), 29 | } 30 | ), 31 | }, 32 | snippet { 33 | "#q", 34 | name = "include qt MOC", 35 | dscr = "#include qt generated MOC file", 36 | mode = "bA", 37 | lang = "cpp", 38 | nodes = { 39 | t((function() 40 | local filename = vim.fn.expand("%:t") 41 | return ('#include "moc_%s"'):format(filename) 42 | end)()), 43 | }, 44 | }, 45 | } 46 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/snippets/cpp/selection.lua: -------------------------------------------------------------------------------- 1 | local ls = require("luasnip") 2 | ---@type luasnip-snippets.nodes 3 | local Nodes = require("luasnip-snippets.nodes") 4 | local snippet = Nodes.construct_snippet 5 | local i = Nodes.insert_node 6 | local c = Nodes.choice_node 7 | local fmta = require("luasnip.extras.fmt").fmta 8 | local f = ls.function_node 9 | local t = ls.text_node 10 | local rep = require("luasnip.extras").rep 11 | local CommonCond = require("luasnip-snippets.utils.common_cond") 12 | ---@type luasnip-snippets.utils.cond 13 | local Cond = require("luasnip-snippets.utils.cond") 14 | ---@type luasnip-snippets.config 15 | local Config = require("luasnip-snippets.config") 16 | ---@type luasnip-snippets.utils 17 | local Utils = require("luasnip-snippets.utils") 18 | local CppCommons = require("luasnip-snippets.snippets.cpp.commons") 19 | 20 | local has_select_raw = CommonCond.has_select_raw 21 | 22 | return { 23 | snippet { 24 | "#if", 25 | name = "(#if) #if ... #endif", 26 | dscr = "Wrap selected code in #if ... #endif block", 27 | mode = "bw", 28 | lang = "cpp", 29 | cond = has_select_raw, 30 | nodes = fmta( 31 | [[ 32 | #if 33 | 34 | #endif // 35 | ]], 36 | { 37 | condition = i(1, "condition"), 38 | cursor = i(0), 39 | selected = f(function(_, snip) 40 | local _, env = {}, snip.env 41 | return env.LS_SELECT_RAW 42 | end), 43 | condition_r = rep(1), 44 | } 45 | ), 46 | }, 47 | 48 | snippet { 49 | "if", 50 | name = "(if) if (...) { ... }", 51 | dscr = "Wrap selected code in if (...) { ... } block", 52 | mode = "bw", 53 | lang = "cpp", 54 | cond = has_select_raw, 55 | nodes = fmta( 56 | [[ 57 | if () { 58 | 59 | } 60 | ]], 61 | { 62 | condition = i(1, "condition"), 63 | cursor = i(0), 64 | selected = f(function(_, snip) 65 | local _, env = {}, snip.env 66 | return CppCommons.fix_leading_whitespace(env.LS_SELECT_RAW) 67 | end), 68 | } 69 | ), 70 | }, 71 | 72 | snippet { 73 | "do", 74 | name = "(do) do { ... } while (0)", 75 | dscr = "Wrap selected code in do { ... } while (0) block", 76 | mode = "bw", 77 | lang = "cpp", 78 | cond = has_select_raw, 79 | nodes = fmta( 80 | [[ 81 | do { 82 | 83 | } while (0); 84 | ]], 85 | { 86 | cursor = i(0), 87 | selected = f(function(_, snip) 88 | local _, env = {}, snip.env 89 | return CppCommons.fix_leading_whitespace(env.LS_SELECT_RAW) 90 | end), 91 | } 92 | ), 93 | }, 94 | 95 | snippet { 96 | "while", 97 | name = "(while) while (...) { ... }", 98 | dscr = "Wrap selected code in while (...) { ... } block", 99 | mode = "bw", 100 | lang = "cpp", 101 | cond = has_select_raw, 102 | nodes = fmta( 103 | [[ 104 | while () { 105 | 106 | } 107 | ]], 108 | { 109 | condition = i(1, "condition"), 110 | cursor = i(0), 111 | selected = f(function(_, snip) 112 | local _, env = {}, snip.env 113 | return CppCommons.fix_leading_whitespace(env.LS_SELECT_RAW) 114 | end), 115 | } 116 | ), 117 | }, 118 | 119 | snippet { 120 | "#de", 121 | name = "(#de) #define ...", 122 | dscr = "Wrap selected code in #define block", 123 | mode = "bw", 124 | lang = "cpp", 125 | cond = has_select_raw, 126 | nodes = fmta( 127 | [[ 128 | #define () \ 129 | 130 | ]], 131 | { 132 | name = i(1, "condition"), 133 | cursor = i(0), 134 | selected = f(function(_, snip) 135 | local _, env = {}, snip.env 136 | return CppCommons.fix_leading_whitespace( 137 | CppCommons.add_trailing_slash(env.LS_SELECT_RAW) 138 | ) 139 | end), 140 | } 141 | ), 142 | }, 143 | } 144 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/snippets/cpp/statements.lua: -------------------------------------------------------------------------------- 1 | local ls = require("luasnip") 2 | local UtilsTS = require("luasnip-snippets.utils.treesitter") 3 | local d = ls.dynamic_node 4 | local sn = ls.snippet_node 5 | local t = ls.text_node 6 | local fmta = require("luasnip.extras.fmt").fmta 7 | local snippet = require("luasnip-snippets.nodes").construct_snippet 8 | local i = require("luasnip-snippets.nodes").insert_node 9 | local c = require("luasnip-snippets.nodes").choice_node 10 | local rep = require("luasnip.extras").rep 11 | 12 | local function inject_class_name(_, line_to_cursor, match, captures) 13 | -- check if at the line begin 14 | if not line_to_cursor:sub(1, -(#match + 1)):match("^%s*$") then 15 | return nil 16 | end 17 | 18 | local row, col = unpack(vim.api.nvim_win_get_cursor(0)) 19 | local buf = vim.api.nvim_get_current_buf() 20 | 21 | return UtilsTS.invoke_after_reparse_buffer( 22 | buf, 23 | match, 24 | function(parser, source) 25 | local pos = { 26 | row - 1, 27 | col - #match, -- match has been removed from source 28 | } 29 | local node = parser:named_node_for_range { 30 | pos[1], 31 | pos[2], 32 | pos[1], 33 | pos[2], 34 | } 35 | if node == nil then 36 | return nil 37 | end 38 | 39 | local class_node = UtilsTS.find_first_parent(node, { 40 | "struct_specifier", 41 | "class_specifier", 42 | }) 43 | if class_node == nil then 44 | return nil 45 | end 46 | local name_nodes = class_node:field("name") 47 | if name_nodes == nil or #name_nodes == 0 then 48 | return nil 49 | end 50 | local name_node = name_nodes[1] 51 | local ret = { 52 | trigger = match, 53 | captures = captures, 54 | env_override = { 55 | CLASS_NAME = vim.treesitter.get_node_text(name_node, source), 56 | }, 57 | } 58 | return ret 59 | end 60 | ) 61 | end 62 | 63 | ---Construct class constructors 64 | ---@param trig string 65 | ---@param template string 66 | local function constructor_snip(trig, name, template) 67 | return ls.s( 68 | { 69 | trig = trig, 70 | name = ("(%s) %s"):format(trig, name), 71 | wordTrig = true, 72 | trigEngine = "plain", 73 | hidden = true, 74 | snippetType = "autosnippet", 75 | -- condition = cond and cond.condition, 76 | -- show_condition = cond and cond.show_condition, 77 | resolveExpandParams = inject_class_name, 78 | }, 79 | d(1, function(_, parent) 80 | local env = parent.env 81 | return sn( 82 | nil, 83 | fmta(template, { 84 | cls = t(env.CLASS_NAME), 85 | }) 86 | ) 87 | end) 88 | ) 89 | end 90 | 91 | return { 92 | constructor_snip( 93 | "ctor!", 94 | "Default constructor", 95 | [[ 96 | () = default; 97 | ]] 98 | ), 99 | constructor_snip( 100 | "dtor!", 101 | "Default destructor", 102 | [[ 103 | ~() = default; 104 | ]] 105 | ), 106 | constructor_snip( 107 | "cc!", 108 | "Copy constructor", 109 | [[ 110 | (const & rhs) = default; 111 | ]] 112 | ), 113 | constructor_snip( 114 | "mv!", 115 | "Move constructor", 116 | [[ 117 | (&& rhs) = default; 118 | ]] 119 | ), 120 | constructor_snip( 121 | "ncc!", 122 | "No copy constructor", 123 | [[ 124 | (const &) = delete; 125 | ]] 126 | ), 127 | constructor_snip( 128 | "nmv!", 129 | "No move constructor", 130 | [[ 131 | (&&) = delete; 132 | ]] 133 | ), 134 | constructor_snip( 135 | "ncm!", 136 | "No copy and move constructor", 137 | [[ 138 | (const &) = delete; 139 | (&&) = delete; 140 | ]] 141 | ), 142 | snippet { 143 | "itf", 144 | name = "Interface", 145 | dscr = "Declare interface", 146 | mode = "bw", 147 | nodes = fmta( 148 | [[ 149 | struct <> { 150 | virtual ~<>() = default; 151 | 152 | <> 153 | }; 154 | ]], 155 | { 156 | i(1, "Interface"), 157 | rep(1), 158 | i(0), 159 | } 160 | ), 161 | }, 162 | snippet { 163 | "pvf", 164 | name = "Pure virtual function", 165 | dscr = "Declare pure virtual function", 166 | mode = "bw", 167 | resolveExpandParams = inject_class_name, 168 | nodes = fmta("virtual () = 0;", { 169 | name = i(1, "func", { dscr = "Function name" }), 170 | args = i(2, "args", { dscr = "Function arguments" }), 171 | specifier = c(3, { t(""), t("const") }, { dscr = "Function specifier" }), 172 | ret_t = i(4, "void", { dscr = "Return type" }), 173 | }), 174 | }, 175 | } 176 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/snippets/dart/_treesitter.lua: -------------------------------------------------------------------------------- 1 | local UtilsTS = require("luasnip-snippets.utils.treesitter") 2 | 3 | ---@class LSSnippets.snippet.dart.Declaration 4 | ---@field ty string 5 | ---@field nullable boolean 6 | ---@field identifier string 7 | ---@field final boolean 8 | 9 | --[[ 10 | declaration [11, 2] - [11, 23] 11 | type_identifier [11, 2] - [11, 6] 12 | type_arguments [11, 6] - [11, 11] 13 | type_identifier [11, 7] - [11, 10] 14 | nullable_type [11, 11] - [11, 12] 15 | initialized_identifier_list [11, 13] - [11, 23] 16 | initialized_identifier [11, 13] - [11, 17] 17 | identifier [11, 13] - [11, 17] 18 | initialized_identifier [11, 19] - [11, 23] 19 | identifier [11, 19] - [11, 23] 20 | ]] 21 | 22 | ---@param node TSNode 23 | ---@param source string|number 24 | ---@return LSSnippets.snippet.dart.Declaration[] 25 | local function _handle_declaration(node, source) 26 | local ty 27 | local nullable = false 28 | local final = false 29 | local fields = {} 30 | 31 | for c in node:iter_children() do 32 | if c:type() == "type_identifier" then 33 | ty = vim.treesitter.get_node_text(c, source) 34 | elseif c:type() == "type_arguments" then 35 | ty = ty .. vim.treesitter.get_node_text(c, source) 36 | elseif c:type() == "nullable_type" then 37 | nullable = true 38 | elseif c:type() == "final_builtin" then 39 | final = true 40 | elseif c:type() == "initialized_identifier_list" then 41 | for cc in c:iter_children() do 42 | if cc:type() == "initialized_identifier" then 43 | local id_node = cc:child(0) 44 | assert(id_node ~= nil) 45 | fields[#fields + 1] = vim.treesitter.get_node_text(id_node, source) 46 | end 47 | end 48 | end 49 | end 50 | 51 | local ret = {} 52 | for _, field in ipairs(fields) do 53 | ret[#ret + 1] = { 54 | ty = ty, 55 | nullable = nullable, 56 | final = final, 57 | identifier = field, 58 | } 59 | end 60 | 61 | return ret 62 | end 63 | 64 | ---@param _ any 65 | ---@param line_to_cursor string 66 | ---@param match string 67 | ---@param captures any 68 | local function resolve_class_decls(_, line_to_cursor, match, captures) 69 | -- check if at the line begin 70 | if not line_to_cursor:sub(1, -(#match + 1)):match("^%s*$") then 71 | return nil 72 | end 73 | 74 | local row, col = unpack(vim.api.nvim_win_get_cursor(0)) 75 | local buf = vim.api.nvim_get_current_buf() 76 | ---@param parser LanguageTree 77 | ---@param source number|string 78 | return UtilsTS.invoke_after_reparse_buffer( 79 | buf, 80 | match, 81 | function(parser, source) 82 | local pos = { 83 | row - 1, 84 | col - #match, 85 | } 86 | local node = 87 | parser:named_node_for_range { pos[1], pos[2], pos[1], pos[2] } 88 | if node == nil then 89 | return nil 90 | end 91 | local class_node = UtilsTS.find_first_parent(node, "class_definition") 92 | if class_node == nil then 93 | return nil 94 | end 95 | 96 | local name = class_node:field("name") 97 | if name == nil or #name == 0 then 98 | return nil 99 | end 100 | local class_name = vim.treesitter.get_node_text(name[1], source) 101 | 102 | local body = class_node:field("body") 103 | if body == nil or #body == 0 then 104 | return nil 105 | end 106 | 107 | local decls = {} 108 | 109 | for c in body[1]:iter_children() do 110 | if c:type() == "declaration" then 111 | vim.list_extend(decls, _handle_declaration(c, source)) 112 | end 113 | end 114 | 115 | return { 116 | trigger = match, 117 | captures = captures, 118 | env_override = { 119 | CLASS_NAME = class_name, 120 | CLASS_DECLS = decls, 121 | }, 122 | } 123 | end 124 | ) 125 | end 126 | 127 | ---@param _ any 128 | ---@param line_to_cursor string 129 | ---@param match string 130 | ---@param captures any 131 | local function resolve_maybe_class_decl(_, line_to_cursor, match, captures) 132 | -- check if at the line begin 133 | if not line_to_cursor:sub(1, -(#match + 1)):match("^%s*$") then 134 | return nil 135 | end 136 | 137 | local row, col = unpack(vim.api.nvim_win_get_cursor(0)) 138 | local buf = vim.api.nvim_get_current_buf() 139 | ---@param parser LanguageTree 140 | ---@param source number|string 141 | return UtilsTS.invoke_after_reparse_buffer( 142 | buf, 143 | match, 144 | function(parser, source) 145 | local pos = { 146 | row - 1, 147 | col - #match, 148 | } 149 | local node = 150 | parser:named_node_for_range { pos[1], pos[2], pos[1], pos[2] } 151 | if node == nil then 152 | return nil 153 | end 154 | 155 | local env = {} 156 | 157 | local class_node = UtilsTS.find_first_parent(node, "class_definition") 158 | if class_node == nil then 159 | env.IN_CLASS = false 160 | else 161 | env.IN_CLASS = true 162 | local name = class_node:field("name") 163 | if name == nil or #name == 0 then 164 | return nil 165 | end 166 | env.CLASS_NAME = vim.treesitter.get_node_text(name[1], source) 167 | 168 | local body = class_node:field("body") 169 | if body == nil or #body == 0 then 170 | return nil 171 | end 172 | 173 | local decls = {} 174 | for c in body[1]:iter_children() do 175 | if c:type() == "declaration" then 176 | vim.list_extend(decls, _handle_declaration(c, source)) 177 | end 178 | end 179 | env.CLASS_DECLS = decls 180 | end 181 | 182 | return { 183 | trigger = match, 184 | captures = captures, 185 | env_override = env, 186 | } 187 | end 188 | ) 189 | end 190 | 191 | return { 192 | resolve_class_decls = resolve_class_decls, 193 | resolve_maybe_class_decl = resolve_maybe_class_decl, 194 | } 195 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/snippets/dart/init.lua: -------------------------------------------------------------------------------- 1 | ---@param class_name string 2 | ---@param decls LSSnippets.snippet.dart.Declaration[] 3 | local function _build_constructor(class_name, decls) 4 | local lines = {} 5 | local all_final = true 6 | for _, decl in ipairs(decls) do 7 | if not decl.final then 8 | all_final = false 9 | break 10 | end 11 | end 12 | 13 | if all_final then 14 | lines[#lines + 1] = ("const %s({"):format(class_name) 15 | else 16 | lines[#lines + 1] = ("%s({"):format(class_name) 17 | end 18 | for _, decl in ipairs(decls) do 19 | lines[#lines + 1] = ("%sthis.%s,"):format( 20 | decl.nullable and " " or " required ", 21 | decl.identifier 22 | ) 23 | end 24 | lines[#lines + 1] = "});" 25 | 26 | return lines 27 | end 28 | 29 | return function() 30 | local snippet = require("luasnip-snippets.nodes").construct_snippet 31 | local i = require("luasnip-snippets.nodes").insert_node 32 | local ls = require("luasnip") 33 | local f = ls.function_node 34 | local dart_ts = require("luasnip-snippets.snippets.dart._treesitter") 35 | local sn = ls.snippet_node 36 | local d = ls.dynamic_node 37 | local t = ls.text_node 38 | local fmta = require("luasnip.extras.fmt").fmta 39 | local extras = require("luasnip.extras") 40 | local rep = extras.rep 41 | 42 | return { 43 | snippet { 44 | "ctor!", 45 | name = "(ctor!) constructor", 46 | dscr = "Expands to class constructor", 47 | mode = "bwA", 48 | resolveExpandParams = dart_ts.resolve_class_decls, 49 | nodes = { 50 | f(function(_, parent) 51 | local env = parent.snippet.env 52 | return _build_constructor(env.CLASS_NAME, env.CLASS_DECLS) 53 | end, {}), 54 | }, 55 | }, 56 | 57 | snippet { 58 | "js!", 59 | name = "(js!) $_xxxFromJson/ToJson() methods", 60 | dscr = "Expands to common json-related(FromJson/ToJson) methods", 61 | mode = "bwA", 62 | resolveExpandParams = dart_ts.resolve_maybe_class_decl, 63 | nodes = d(1, function(_, parent) 64 | local env = parent.env 65 | if env.IN_CLASS then 66 | local lines = { 67 | "factory %s.fromJson(Map json) =>", 68 | "_$%sFromJson(json);", 69 | "Map toJson() => _$%sToJson(this);", 70 | } 71 | local ret = {} 72 | for _, value in ipairs(lines) do 73 | ret[#ret + 1] = value:format(env.CLASS_NAME) 74 | end 75 | return sn(nil, t(ret)) 76 | else 77 | return sn( 78 | nil, 79 | fmta( 80 | [[ 81 | @JsonSerializable() 82 | class { 83 | factory .fromJson(Map<> json) =>> 84 | _$FromJson(json); 85 | Map<> toJson() =>> _$ToJson(this); 86 | } 87 | ]], 88 | { 89 | name = i(1, "ClassName"), 90 | rep_name = rep(1), 91 | } 92 | ) 93 | ) 94 | end 95 | end), 96 | }, 97 | 98 | snippet { 99 | "init!", 100 | name = "(init!) initState", 101 | dscr = "Expands to initState() with override marker", 102 | mode = "bwA", 103 | nodes = fmta( 104 | [[ 105 | @override 106 | void initState() { 107 | super.initState(); 108 | 109 | } 110 | ]], 111 | { 112 | body = i(0), 113 | } 114 | ), 115 | }, 116 | 117 | snippet { 118 | "dis!", 119 | name = "(dis!) dispose()", 120 | dscr = "Expands to dispose() with override marker", 121 | mode = "bwA", 122 | nodes = fmta( 123 | [[ 124 | @override 125 | void dispose() { 126 | 127 | super.dispose(); 128 | } 129 | ]], 130 | { 131 | body = i(0), 132 | } 133 | ), 134 | }, 135 | 136 | snippet { 137 | "for!", 138 | name = "(for!) for (... in ...)", 139 | dscr = "Expands to a for loop in variable", 140 | mode = "bwhA", 141 | nodes = fmta( 142 | [[ 143 | for (var item in ) { 144 | 145 | } 146 | ]], 147 | { 148 | iterable = i(1, "Iterable"), 149 | body = i(0), 150 | } 151 | ), 152 | }, 153 | 154 | snippet { 155 | "fn", 156 | name = "(fn) function", 157 | dscr = "Expands to a simple function definition", 158 | mode = "bw", 159 | nodes = fmta( 160 | [[ 161 | () { 162 | 163 | } 164 | ]], 165 | { 166 | body = i(0), 167 | name = i(1, "FuncName", { desc = "function name" }), 168 | args = i(2, "", { desc = "arguments" }), 169 | ret = i(3, "void", { desc = "return type" }), 170 | } 171 | ), 172 | }, 173 | 174 | snippet { 175 | "wfn", 176 | name = "(wfn) widget function", 177 | dscr = "Expands to a function definition returns a Widget", 178 | mode = "bw", 179 | nodes = fmta( 180 | [[ 181 | Widget _build(BuildContext context) { 182 | 183 | } 184 | ]], 185 | { 186 | body = i(0), 187 | name = i(1, "FuncName", { desc = "function name" }), 188 | } 189 | ), 190 | }, 191 | 192 | snippet { 193 | "afn", 194 | name = "(afn) async function", 195 | dscr = "Expands to an async function definition", 196 | mode = "bw", 197 | nodes = fmta( 198 | [[ 199 | Future<<>> () async { 200 | 201 | } 202 | ]], 203 | { 204 | body = i(0), 205 | name = i(1, "FuncName", { desc = "function name" }), 206 | args = i(2, "", { desc = "arguments" }), 207 | ret = i(3, "void", { desc = "return type" }), 208 | } 209 | ), 210 | }, 211 | 212 | snippet { 213 | "sfw!", 214 | name = "(sfw!) StatefulWidget", 215 | dscr = "Expands to a StatefulWidget class", 216 | mode = "bwA", 217 | nodes = fmta( 218 | [[ 219 | class extends StatefulWidget { 220 | const ({super.key}); 221 | 222 | @override 223 | State<<>> createState() =>> _State(); 224 | } 225 | 226 | class _State extends State<<>> { 227 | @override 228 | void initState() { 229 | super.initState(); 230 | // TODO(hawtian): Implement initState 231 | } 232 | 233 | @override 234 | Widget build(BuildContext context) { 235 | // TODO(hawtian): Implement build 236 | throw UnimplementedError(); 237 | } 238 | } 239 | ]], 240 | { 241 | name = i(1, "ClassName"), 242 | rep_name = rep(1), 243 | } 244 | ), 245 | }, 246 | 247 | snippet { 248 | "slw!", 249 | name = "(slw!) StatelessWidget class", 250 | dscr = "Expands to a StatelessWidget class", 251 | mode = "bwA", 252 | nodes = fmta( 253 | [[ 254 | class extends StatelessWidget { 255 | ({super.key}); 256 | 257 | @override 258 | Widget build(BuildContext context) { 259 | // TODO(hawtian): Implement build 260 | throw UnimplementedError(); 261 | } 262 | } 263 | ]], 264 | { 265 | name = i(1, "ClassName"), 266 | rep_name = rep(1), 267 | } 268 | ), 269 | }, 270 | } 271 | end 272 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/snippets/lua/default.lua: -------------------------------------------------------------------------------- 1 | local snippet = require("luasnip-snippets.nodes").construct_snippet 2 | local fmt = require("luasnip.extras.fmt").fmt 3 | local i = require("luasnip-snippets.nodes").insert_node 4 | local ls = require("luasnip") 5 | local f = ls.function_node 6 | local tsp = require("luasnip.extras.treesitter_postfix") 7 | local rep = require("luasnip.extras").rep 8 | 9 | local function last_lua_module_section(args) 10 | local text = args[1][1] or "" 11 | local split = vim.split(text, ".", { plain = true }) 12 | 13 | local options = {} 14 | for len = 0, #split - 1 do 15 | local node = 16 | ls.t(table.concat(vim.list_slice(split, #split - len, #split), "_")) 17 | table.insert(options, node) 18 | end 19 | 20 | return ls.sn(nil, { 21 | ls.c(1, options), 22 | }) 23 | end 24 | 25 | local expr_query = [[ 26 | [ 27 | (function_call) 28 | (identifier) 29 | (expression_list) 30 | (dot_index_expression) 31 | (bracket_index_expression) 32 | ] @prefix 33 | ]] 34 | 35 | return { 36 | snippet { 37 | "fn", 38 | name = "(fn) function", 39 | dscr = "Expands to function definition", 40 | mode = "w", 41 | nodes = fmt( 42 | [[ 43 | function {}({}) 44 | {} 45 | end 46 | ]], 47 | { 48 | i(1, "function name", { desc = "Function Name" }), 49 | i(2, "arguments", { desc = "Function Arguments" }), 50 | i(0), 51 | } 52 | ), 53 | }, 54 | 55 | snippet { 56 | "req", 57 | name = "require(...)", 58 | dscr = "Require statement", 59 | mode = "wb", 60 | nodes = fmt([[local {} = require("{}")]], { 61 | ls.d(2, last_lua_module_section, { 1 }), 62 | i(1, "module"), 63 | }), 64 | }, 65 | 66 | snippet { 67 | '#i', 68 | name = "require(...)", 69 | dscr = "Expands to require statement with type annotation", 70 | mode = "bwA", 71 | nodes = fmt( 72 | [[ 73 | ---@type {} 74 | local {} = require("{}") 75 | ]], 76 | { 77 | rep(1), 78 | f(function(args) 79 | local module_name = args[1][1] 80 | local parts = vim.split(module_name, ".", { 81 | plain = true, 82 | trimempty = true, 83 | }) 84 | module_name = parts[#parts] 85 | parts = vim.split(module_name, "/", { 86 | plain = true, 87 | trimempty = true, 88 | }) 89 | module_name = parts[#parts] 90 | return module_name:gsub("^%l", string.upper) 91 | end, { 1 }), 92 | i(1, "module"), 93 | } 94 | ), 95 | }, 96 | 97 | tsp.treesitter_postfix( 98 | { 99 | trig = ".ipairs", 100 | name = "(.ipairs) for in ipairs(...)", 101 | dscr = "Expands expression to for in ipairs(...) do ... end", 102 | wordTrig = false, 103 | reparseBuffer = "live", 104 | matchTSNode = { 105 | query = expr_query, 106 | query_lang = "lua", 107 | }, 108 | }, 109 | fmt( 110 | [[ 111 | for i, value in ipairs({}) do 112 | {} 113 | end 114 | ]], 115 | { 116 | f(function(_, parent) 117 | local match = parent.snippet.env.LS_TSMATCH 118 | return match 119 | end, {}), 120 | i(0), 121 | } 122 | ) 123 | ), 124 | 125 | tsp.treesitter_postfix( 126 | { 127 | trig = ".pairs", 128 | name = "(.pairs) for in pairs(...)", 129 | dscr = "Expands expression to for in pairs(...) do ... end", 130 | wordTrig = false, 131 | reparseBuffer = "live", 132 | matchTSNode = { 133 | query = expr_query, 134 | query_lang = "lua", 135 | }, 136 | }, 137 | fmt( 138 | [[ 139 | for key, value in pairs({}) do 140 | {} 141 | end 142 | ]], 143 | { 144 | f(function(_, parent) 145 | local match = parent.snippet.env.LS_TSMATCH 146 | return match 147 | end, {}), 148 | i(0), 149 | } 150 | ) 151 | ), 152 | 153 | tsp.treesitter_postfix( 154 | { 155 | trig = ".isnil", 156 | name = "(.isnil) if ... == nil", 157 | dscr = "Expands expression to if ... == nil then ... end", 158 | wordTrig = false, 159 | reparseBuffer = "live", 160 | matchTSNode = { 161 | query = expr_query, 162 | query_lang = "lua", 163 | }, 164 | }, 165 | fmt( 166 | [[ 167 | if {} == nil then 168 | {} 169 | end 170 | ]], 171 | { 172 | f(function(_, parent) 173 | local match = parent.snippet.env.LS_TSMATCH 174 | return match 175 | end, {}), 176 | i(0, "return"), 177 | } 178 | ) 179 | ), 180 | } 181 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/snippets/lua/init.lua: -------------------------------------------------------------------------------- 1 | ---@type luasnip-snippets.utils 2 | local Utils = require("luasnip-snippets.utils") 3 | 4 | ---@type luasnip-snippets.config 5 | local Config = require("luasnip-snippets.config") 6 | 7 | local function setup() 8 | local submodules = { 9 | "default", 10 | } 11 | 12 | if Config.get("snippet.lua.vim_snippet") then 13 | submodules[#submodules + 1] = "vim" 14 | end 15 | 16 | return Utils.concat_snippets("luasnip-snippets.snippets.lua", submodules) 17 | end 18 | 19 | return setup 20 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/snippets/lua/vim.lua: -------------------------------------------------------------------------------- 1 | ---@type luasnip-snippets.nodes 2 | local Nodes = require("luasnip-snippets.nodes") 3 | ---@type luasnip-snippets.config 4 | local Config = require("luasnip-snippets.config") 5 | ---@type luasnip-snippets.utils.cond 6 | local Cond = require("luasnip-snippets.utils.cond") 7 | ---@type luasnip-snippets.utils.treesitter 8 | local UtilsTS = require("luasnip-snippets.utils.treesitter") 9 | ---@type luasnip-snippets.utils 10 | local Utils = require("luasnip-snippets.utils") 11 | 12 | local fmt = require("luasnip.extras.fmt").fmt 13 | local i = require("luasnip-snippets.nodes").insert_node 14 | local ls = require("luasnip") 15 | local f = ls.function_node 16 | local tsp = require("luasnip.extras.treesitter_postfix") 17 | 18 | ---@param opts LSSnippets.SnippetOptions 19 | local function snippet(opts) 20 | local cond = Config.get("snippet.lua.cond") 21 | if cond then 22 | local cond_obj = Cond.make_condition(cond, cond) 23 | local previous_cond = opts.cond 24 | if previous_cond ~= nil then 25 | opts.cond = previous_cond + cond_obj 26 | else 27 | opts.cond = cond_obj 28 | end 29 | end 30 | return Nodes.construct_snippet(opts) 31 | end 32 | 33 | local index_expression_query = [[ 34 | [ 35 | (dot_index_expression) 36 | (bracket_index_expression) 37 | ] @prefix 38 | ]] 39 | 40 | local index_expression_matcher = UtilsTS.make_type_matcher { 41 | "dot_index_expression", 42 | "bracket_index_expression", 43 | } 44 | 45 | ---@param context LSSnippets.ProcessMatchesContext 46 | ---@param previous any 47 | local function inject_matches(context, previous) 48 | local fields = {} 49 | local node = context.prefix_node 50 | while node ~= nil and index_expression_matcher[node:type()] == 1 do 51 | local field = context.ts_parser:get_node_text(node:field("field")[1]) 52 | if node:type() == "dot_index_expression" then 53 | fields[#fields + 1] = ('"%s"'):format(field) 54 | else 55 | fields[#fields + 1] = field 56 | end 57 | node = node:field("table")[1] 58 | end 59 | fields[#fields + 1] = context.ts_parser:get_node_text(node) 60 | Utils.reverse_list(fields) 61 | 62 | previous = vim.tbl_deep_extend("force", previous, { 63 | env_override = { 64 | INDEX_FIELDS = fields, 65 | }, 66 | }) 67 | 68 | return previous 69 | end 70 | 71 | return { 72 | snippet { 73 | "ifn", 74 | name = "(ifn) vim.F.if_nil", 75 | dscr = "Wraps an expression in vim.F.if_nil", 76 | mode = "w", 77 | nodes = fmt("vim.F.if_nil({}, {})", { 78 | i(1, "expr", { desc = "Expression" }), 79 | i(2, "{}", { desc = "Default value" }), 80 | }), 81 | }, 82 | 83 | UtilsTS.treesitter_postfix({ 84 | trig = ".tget", 85 | name = "(.tget) vim.tbl_get(...)", 86 | dscr = "Expands index_expression to vim.tbl_get syntax", 87 | wordTrig = false, 88 | reparseBuffer = "live", 89 | matchTSNode = { 90 | query = index_expression_query, 91 | query_lang = "lua", 92 | }, 93 | injectMatches = inject_matches, 94 | }, { 95 | f(function(_, parent) 96 | local fields = parent.snippet.env.INDEX_FIELDS 97 | return ("vim.tbl_get(%s)"):format(table.concat(fields, ", ")) 98 | end, {}), 99 | }), 100 | } 101 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/snippets/nix.lua: -------------------------------------------------------------------------------- 1 | local ls = require("luasnip") 2 | local f = ls.function_node 3 | local snippet = require("luasnip-snippets.nodes").construct_snippet 4 | local fmta = require("luasnip.extras.fmt").fmta 5 | local extras = require("luasnip.extras") 6 | local rep = extras.rep 7 | local i = require("luasnip-snippets.nodes").insert_node 8 | local tsp = require("luasnip.extras.treesitter_postfix") 9 | local Utils = require("luasnip-snippets.utils") 10 | ---@type luasnip-snippets.utils.treesitter 11 | local UtilsTS = require("luasnip-snippets.utils.treesitter") 12 | 13 | local identifier_query = [[ 14 | [ 15 | (identifier) 16 | ] @prefix 17 | ]] 18 | 19 | local function identifier_tsp(trig, expand, dscr) 20 | local name = ("(%s) %s"):format(trig, expand) 21 | if dscr == nil then 22 | dscr = ("Wraps an expression with %s"):format(expand) 23 | else 24 | dscr = dscr:format(expand) 25 | end 26 | local replaced = expand:gsub("?", "%%s") 27 | 28 | return tsp.treesitter_postfix({ 29 | trig = trig, 30 | name = name, 31 | dscr = dscr, 32 | wordTrig = false, 33 | reparseBuffer = "live", 34 | matchTSNode = { 35 | query = identifier_query, 36 | query_lang = "nix", 37 | }, 38 | }, { 39 | f(function(_, parent) 40 | return Utils.replace_all(parent.snippet.env.LS_TSMATCH, replaced) 41 | end, {}), 42 | }) 43 | end 44 | 45 | local bind_query = [[ 46 | [ 47 | ((binding 48 | expression: (_) @expr 49 | )) 50 | ] @prefix 51 | ]] 52 | 53 | ---@param context LSSnippets.ProcessMatchesContext 54 | ---@param previous any 55 | local function inject_bind_matches(context, previous) 56 | vim.print("???") 57 | local node = context.prefix_node 58 | local attr_path = node:field("attrpath")[1] 59 | local attrs_nodes = attr_path:field("attr") 60 | local attrs = {} 61 | 62 | for _, attr in ipairs(attrs_nodes) do 63 | local attr_text = context.ts_parser:get_node_text(attr) 64 | attrs[#attrs + 1] = attr_text 65 | end 66 | 67 | previous = vim.tbl_deep_extend("force", previous, { 68 | env_override = { 69 | ATTRS = attrs, 70 | }, 71 | }) 72 | 73 | return previous 74 | end 75 | 76 | return { 77 | snippet { 78 | "@module", 79 | name = "(@module) ...", 80 | dscr = "Expands to a common module skeleton", 81 | mode = "bw", 82 | nodes = fmta( 83 | [[ 84 | { 85 | config, 86 | lib, 87 | pkgs, 88 | ... 89 | }: let 90 | cfg = config.; 91 | in { 92 | options. = { 93 | enable = lib.mkEnableOption "Enable module "; 94 | }; 95 | 96 | config = lib.mkIf cfg.enable { 97 | }; 98 | } 99 | ]], 100 | { 101 | module = i(1, "module", { desc = "Module name" }), 102 | module_r = rep(1), 103 | } 104 | ), 105 | }, 106 | 107 | identifier_tsp( 108 | ".on", 109 | "? = { enable = true; };", 110 | "Completes an identifier with an enable option" 111 | ), 112 | 113 | UtilsTS.treesitter_postfix({ 114 | trig = ".split", 115 | name = "(.split) foo.bar = xxx; -> foo = { bar = xxx; };", 116 | dscr = "Split a dot expression into full attrset declaration", 117 | wordTrig = false, 118 | reparseBuffer = "live", 119 | matchTSNode = { 120 | query = bind_query, 121 | query_lang = "nix", 122 | }, 123 | injectMatches = inject_bind_matches, 124 | }, { 125 | f(function(_, parent) 126 | vim.print(parent.snippet.env) 127 | local attrs = parent.snippet.env.ATTRS 128 | local expr = table.concat(parent.snippet.env.LS_TSCAPTURE_EXPR, "\n") 129 | Utils.reverse_list(attrs) 130 | 131 | local generate_bindings = function(first, attr, previous) 132 | if first then 133 | return ("%s = %s;"):format(attr, previous) 134 | else 135 | return ("%s = { %s };"):format(attr, previous) 136 | end 137 | end 138 | 139 | for j, attr in ipairs(attrs) do 140 | expr = generate_bindings(j == 1, attr, expr) 141 | end 142 | 143 | return expr 144 | end, {}), 145 | }), 146 | } 147 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/snippets/rust/default.lua: -------------------------------------------------------------------------------- 1 | local ls = require("luasnip") 2 | local t = ls.text_node 3 | local snippet = require("luasnip-snippets.nodes").construct_snippet 4 | 5 | return { 6 | snippet { 7 | "pc", 8 | name = "(pc) pub(crate) ...", 9 | dscr = "Expands to pub(crate) visibility", 10 | mode = "bw", 11 | nodes = { 12 | t("pub(crate) "), 13 | }, 14 | }, 15 | snippet { 16 | "ps", 17 | name = "(ps) pub(super) ...", 18 | dscr = "Expands to pub(super) visibility", 19 | mode = "bw", 20 | nodes = { 21 | t("pub(super) "), 22 | }, 23 | }, 24 | snippet { 25 | "ii", 26 | name = "(ii) #[inline] ...", 27 | dscr = "Expands to #[inline] attribute", 28 | mode = "bw", 29 | nodes = { 30 | t("#[inline]"), 31 | }, 32 | }, 33 | snippet { 34 | "ia", 35 | name = "(ia) #[inline(always)] ...", 36 | dscr = "Expands to #[inline(always)] attribute", 37 | mode = "bw", 38 | nodes = { 39 | t("#[inline(always)]"), 40 | }, 41 | }, 42 | } 43 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/snippets/rust/impl_block.lua: -------------------------------------------------------------------------------- 1 | local UtilsTS = require("luasnip-snippets.utils.treesitter") 2 | local ls = require("luasnip") 3 | local t = ls.text_node 4 | local fmta = require("luasnip.extras.fmt").fmta 5 | local i = require("luasnip-snippets.nodes").insert_node 6 | local c = require("luasnip-snippets.nodes").choice_node 7 | 8 | local function require_impl_block(_, _, match, captures) 9 | local row, col = unpack(vim.api.nvim_win_get_cursor(0)) 10 | local buf = vim.api.nvim_get_current_buf() 11 | 12 | return UtilsTS.invoke_after_reparse_buffer(buf, match, function(parser, _) 13 | local pos = { 14 | row - 1, 15 | col - #match, 16 | } 17 | local node = parser:named_node_for_range { 18 | pos[1], 19 | pos[2], 20 | pos[1], 21 | pos[2], 22 | } 23 | 24 | local ret = { 25 | trigger = match, 26 | capture = captures, 27 | env_override = { 28 | IMPL_ITEM_START = UtilsTS.start_pos( 29 | UtilsTS.find_first_parent(node, { "impl_item" }) 30 | ), 31 | FUNCTION_ITEM_START = UtilsTS.start_pos( 32 | UtilsTS.find_first_parent(node, { "function_item" }) 33 | ), 34 | CLOSURE_EXPRESSION_START = UtilsTS.start_pos( 35 | UtilsTS.find_first_parent(node, { "closure_expression" }) 36 | ), 37 | }, 38 | } 39 | 40 | vim.api.nvim_win_set_cursor(0, { row, col }) 41 | 42 | if 43 | ret.env_override.IMPL_ITEM_START ~= nil 44 | and ret.env_override.FUNCTION_ITEM_START == nil 45 | and ret.env_override.CLOSURE_EXPRESSION_START == nil 46 | then 47 | return ret 48 | end 49 | 50 | return nil 51 | end) 52 | end 53 | 54 | return { 55 | ls.s( 56 | { 57 | trig = "pm", 58 | wordTrig = true, 59 | name = "(pm) pub method", 60 | resolveExpandParams = require_impl_block, 61 | }, 62 | fmta( 63 | [[ 64 | pub fn (<_self>) { 65 | 66 | } 67 | ]], 68 | { 69 | body = i(0), 70 | name = i(1, "new_fn", { desc = "function name" }), 71 | _self = c(2, { 72 | t("&self"), 73 | t("&mut self"), 74 | t("self"), 75 | }, { desc = "self" }), 76 | } 77 | ) 78 | ), 79 | } 80 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/snippets/rust/init.lua: -------------------------------------------------------------------------------- 1 | local Utils = require("luasnip-snippets.utils") 2 | 3 | local function setup() 4 | return Utils.concat_snippets("luasnip-snippets.snippets.rust", { 5 | "default", 6 | "postfix", 7 | "lambda_fn", 8 | "test_fn", 9 | "impl_block", 10 | }) 11 | end 12 | 13 | return setup 14 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/snippets/rust/lambda_fn.lua: -------------------------------------------------------------------------------- 1 | local UtilsTS = require("luasnip-snippets.utils.treesitter") 2 | local ls = require("luasnip") 3 | local d = ls.dynamic_node 4 | local sn = ls.snippet_node 5 | local t = ls.text_node 6 | local fmta = require("luasnip.extras.fmt").fmta 7 | local i = require("luasnip-snippets.nodes").insert_node 8 | local c = require("luasnip-snippets.nodes").choice_node 9 | 10 | local function inject_expanding_environment(_, _, match, captures) 11 | local row, col = unpack(vim.api.nvim_win_get_cursor(0)) 12 | local buf = vim.api.nvim_get_current_buf() 13 | 14 | return UtilsTS.invoke_after_reparse_buffer(buf, match, function(parser, _) 15 | local pos = { 16 | row - 1, 17 | col - #match, 18 | } 19 | local node = parser:named_node_for_range { 20 | pos[1], 21 | pos[2], 22 | pos[1], 23 | pos[2], 24 | } 25 | 26 | local ret = { 27 | trigger = match, 28 | capture = captures, 29 | env_override = { 30 | IMPL_ITEM_START = UtilsTS.start_pos( 31 | UtilsTS.find_first_parent(node, { "impl_item" }) 32 | ), 33 | FUNCTION_ITEM_START = UtilsTS.start_pos( 34 | UtilsTS.find_first_parent(node, { "function_item" }) 35 | ), 36 | CLOSURE_EXPRESSION_START = UtilsTS.start_pos( 37 | UtilsTS.find_first_parent(node, { "closure_expression" }) 38 | ), 39 | }, 40 | } 41 | 42 | vim.api.nvim_win_set_cursor(0, { row, col }) 43 | return ret 44 | end) 45 | end 46 | 47 | ---@class LSSnippets.Rust.Fn.Env 48 | ---@field IMPL_ITEM_START? { [1]: number, [2]: number } 49 | ---@field FUNCTION_ITEM_START? { [1]: number, [2]: number } 50 | ---@field CLOSURE_EXPRESSION_START? { [1]: number, [2]: number } 51 | 52 | return { 53 | ls.s( 54 | { 55 | trig = "fn", 56 | wordTrig = true, 57 | name = "(fn) Function-Definition/Lambda", 58 | resolveExpandParams = inject_expanding_environment, 59 | }, 60 | d(1, function(_, parent) 61 | local env = parent.env 62 | if 63 | env.FUNCTION_ITEM_START ~= nil or env.CLOSURE_EXPRESSION_START ~= nil 64 | then 65 | -- closure expression 66 | return sn( 67 | nil, 68 | fmta( 69 | [[ 70 | || { 71 | 72 | } 73 | ]], 74 | { 75 | modifier = c(1, { 76 | t(""), 77 | t("async "), 78 | t("move "), 79 | }, { desc = "function modifier" }), 80 | args = i(2, "args"), 81 | body = i(0), 82 | } 83 | ) 84 | ) 85 | else 86 | -- function item 87 | return sn( 88 | nil, 89 | fmta( 90 | [[ 91 | fn () { 92 | 93 | } 94 | ]], 95 | { 96 | modifier = c(1, { 97 | t(""), 98 | t("async "), 99 | }, { desc = "function modifier" }), 100 | visible = c(2, { 101 | t(""), 102 | t("pub "), 103 | t("pub(crate) "), 104 | t("pub(super) "), 105 | }, { desc = "visibility" }), 106 | name = i(3, "new_fn", { desc = "function name" }), 107 | args = i(4, "args", { desc = "function arguments" }), 108 | body = i(0), 109 | } 110 | ) 111 | ) 112 | end 113 | end, {}) 114 | ), 115 | } 116 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/snippets/rust/postfix.lua: -------------------------------------------------------------------------------- 1 | local ls = require("luasnip") 2 | local f = ls.function_node 3 | local tsp = require("luasnip.extras.treesitter_postfix") 4 | local Utils = require("luasnip-snippets.utils") 5 | 6 | local expr_query = [[ 7 | [ 8 | (struct_expression) 9 | (unit_expression) 10 | (call_expression) 11 | (identifier) 12 | (field_expression) 13 | (integer_literal) 14 | (string_literal) 15 | ] @prefix 16 | ]] 17 | 18 | local expr_or_type_query = [[ 19 | [ 20 | (struct_expression) 21 | (call_expression) 22 | (unit_expression) 23 | (identifier) 24 | (field_expression) 25 | (integer_literal) 26 | (string_literal) 27 | 28 | (type_identifier) 29 | (generic_type) 30 | (scoped_type_identifier) 31 | (reference_type) 32 | (primitive_type) 33 | ] @prefix 34 | ]] 35 | 36 | local expr_node_types = { 37 | ["struct_expression"] = true, 38 | ["call_expression"] = true, 39 | ["identifier"] = true, 40 | ["field_expression"] = true, 41 | ["integer_literal"] = true, 42 | ["string_literal"] = true, 43 | } 44 | 45 | local function expr_or_type_tsp(trig, typename, expr_callback, type_callback) 46 | local name = ("(%s) %s"):format(trig, typename) 47 | local dscr = ("Wrap expression/type with %s"):format(typename) 48 | return tsp.treesitter_postfix({ 49 | trig = trig, 50 | name = name, 51 | dscr = dscr, 52 | wordTrig = false, 53 | reparseBuffer = "live", 54 | matchTSNode = { 55 | query = expr_or_type_query, 56 | query_lang = "rust", 57 | }, 58 | }, { 59 | f(function(_, parent) 60 | local env = parent.snippet.env 61 | local data = env.LS_TSDATA 62 | if expr_node_types[data.prefix.type] then 63 | -- is expr 64 | return expr_callback(env.LS_TSMATCH) 65 | else 66 | -- is type 67 | return type_callback(env.LS_TSMATCH) 68 | end 69 | end), 70 | }) 71 | end 72 | 73 | local function result_ok_type_callback(match) 74 | return Utils.replace_all(match, "Result<%s, _>") 75 | end 76 | 77 | local function result_err_type_callback(match) 78 | return Utils.replace_all(match, "Result<_, %s>") 79 | end 80 | 81 | local function build_simple_replace_callback(replaced) 82 | return function(match) 83 | return Utils.replace_all(match, replaced) 84 | end 85 | end 86 | 87 | local function new_expr_or_type_tsp(trig, typename) 88 | local expr_callback = function(match) 89 | return Utils.replace_all(match, typename .. "::new(%s)") 90 | end 91 | local type_callback = function(match) 92 | return Utils.replace_all(match, typename .. "<%s>") 93 | end 94 | return expr_or_type_tsp(trig, typename, expr_callback, type_callback) 95 | end 96 | 97 | local function both_replace_expr_or_type_tsp(trig, pattern) 98 | local template = pattern:gsub("?", "%%s") 99 | return expr_or_type_tsp( 100 | trig, 101 | pattern, 102 | build_simple_replace_callback(template), 103 | build_simple_replace_callback(template) 104 | ) 105 | end 106 | 107 | return { 108 | new_expr_or_type_tsp(".rc", "Rc"), 109 | new_expr_or_type_tsp(".arc", "Arc"), 110 | new_expr_or_type_tsp(".box", "Box"), 111 | new_expr_or_type_tsp(".mu", "Mutex"), 112 | new_expr_or_type_tsp(".rw", "RwLock"), 113 | new_expr_or_type_tsp(".cell", "Cell"), 114 | new_expr_or_type_tsp(".refcell", "RefCell"), 115 | both_replace_expr_or_type_tsp(".ref", "&?"), 116 | both_replace_expr_or_type_tsp(".refm", "&mut ?"), 117 | expr_or_type_tsp( 118 | ".ok", 119 | "Ok(?)", 120 | build_simple_replace_callback("Ok(%s)"), 121 | result_ok_type_callback 122 | ), 123 | expr_or_type_tsp( 124 | ".err", 125 | "Err(?)", 126 | build_simple_replace_callback("Err(%s)"), 127 | result_err_type_callback 128 | ), 129 | expr_or_type_tsp( 130 | ".some", 131 | "Some(?)", 132 | build_simple_replace_callback("Some(%s)"), 133 | build_simple_replace_callback("Option<%s>") 134 | ), 135 | 136 | tsp.treesitter_postfix({ 137 | trig = ".println", 138 | name = [[(.println) println!("{:?}", ?)]], 139 | dscr = [[Wrap expression with println!("{:?}", ?)]], 140 | wordTrig = false, 141 | reparseBuffer = "live", 142 | matchTSNode = { 143 | query = expr_query, 144 | query_lang = "rust", 145 | }, 146 | }, { 147 | f(function(_, parent) 148 | return Utils.replace_all( 149 | parent.snippet.env.LS_TSMATCH, 150 | [[println!("{:?}", %s)]] 151 | ) 152 | end, {}), 153 | }), 154 | 155 | tsp.treesitter_postfix({ 156 | trig = ".match", 157 | name = [[(.match) match ?]], 158 | dscr = [[Wrap expression with match ? block]], 159 | wordTrig = false, 160 | reparseBuffer = "live", 161 | matchTSNode = { 162 | query = expr_query, 163 | query_lang = "rust", 164 | }, 165 | }, { 166 | f(function(_, parent) 167 | return Utils.replace_all( 168 | parent.snippet.env.LS_TSMATCH, 169 | [[match %s { 170 | }]] 171 | ) 172 | end, {}), 173 | }), 174 | } 175 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/snippets/rust/test_fn.lua: -------------------------------------------------------------------------------- 1 | local UtilsTS = require("luasnip-snippets.utils.treesitter") 2 | local Config = require("luasnip-snippets.config") 3 | local UtilsTbl = require("luasnip-snippets.utils.tbl") 4 | local ls = require("luasnip") 5 | local d = ls.dynamic_node 6 | local sn = ls.snippet_node 7 | local t = ls.text_node 8 | local f = ls.function_node 9 | local fmta = require("luasnip.extras.fmt").fmta 10 | local i = require("luasnip-snippets.nodes").insert_node 11 | local c = require("luasnip-snippets.nodes").choice_node 12 | 13 | ---@param left string[] 14 | ---@param right string[] 15 | ---@param sep string 16 | ---@return string[] 17 | local function dot_concat(left, right, sep) 18 | local ret = {} 19 | 20 | for _, l in ipairs(left) do 21 | for _, r in ipairs(right) do 22 | ret[#ret + 1] = l .. sep .. r 23 | end 24 | end 25 | 26 | return ret 27 | end 28 | 29 | ---@param node TSNode? 30 | ---@return string[] 31 | local function flat_scoped_use_list(source, node) 32 | if node == nil then 33 | return {} 34 | end 35 | if node:type() ~= "scoped_use_list" then 36 | return { 37 | vim.treesitter.get_node_text(node, source), 38 | } 39 | end 40 | 41 | local path_nodes = node:field("path") 42 | if #path_nodes == 0 then 43 | return {} 44 | end 45 | 46 | local paths = {} 47 | for _, path_node in ipairs(path_nodes) do 48 | vim.list_extend(paths, flat_scoped_use_list(source, path_node)) 49 | end 50 | 51 | local items = {} 52 | local name_nodes = node:field("name") 53 | for _, name_node in ipairs(name_nodes) do 54 | vim.list_extend(items, flat_scoped_use_list(source, name_node)) 55 | end 56 | local list_nodes = node:field("list") 57 | local allow_list = { 58 | scoped_use_list = 1, 59 | use_wildcard = 1, 60 | identifier = 1, 61 | } 62 | for _, list_node in ipairs(list_nodes) do 63 | for child in list_node:iter_children() do 64 | if allow_list[child:type()] == 1 then 65 | vim.list_extend(items, flat_scoped_use_list(source, child)) 66 | end 67 | end 68 | end 69 | 70 | return dot_concat(paths, items, "::") 71 | end 72 | 73 | local function inject_expanding_environment(_, _, match, captures) 74 | local row, col = unpack(vim.api.nvim_win_get_cursor(0)) 75 | local buf = vim.api.nvim_get_current_buf() 76 | 77 | return UtilsTS.invoke_after_reparse_buffer( 78 | buf, 79 | match, 80 | function(parser, source) 81 | local pos = { 82 | row - 1, 83 | col - #match, 84 | } 85 | local node = parser:named_node_for_range { 86 | pos[1], 87 | pos[2], 88 | pos[1], 89 | pos[2], 90 | } 91 | 92 | local ret = { 93 | trigger = match, 94 | capture = captures, 95 | env_override = { 96 | IMPL_ITEM_START = UtilsTS.start_pos( 97 | UtilsTS.find_first_parent(node, { "impl_item" }) 98 | ), 99 | FUNCTION_ITEM_START = UtilsTS.start_pos( 100 | UtilsTS.find_first_parent(node, { "function_item" }) 101 | ), 102 | CLOSURE_EXPRESSION_START = UtilsTS.start_pos( 103 | UtilsTS.find_first_parent(node, { "closure_expression" }) 104 | ), 105 | }, 106 | } 107 | 108 | ---@type TSNode? 109 | local mod_item = UtilsTS.find_first_parent(node, { "mod_item" }) 110 | if mod_item ~= nil then 111 | ret.env_override["MOD_ITEM_START"] = 112 | UtilsTS.start_pos(UtilsTS.find_first_parent(node, { "mod_item" })) 113 | local name_node = mod_item:field("name")[1] 114 | if name_node ~= nil then 115 | ret.env_override["MOD_ITEM_NAME"] = 116 | vim.treesitter.get_node_text(name_node, source) 117 | end 118 | local prev = mod_item:prev_sibling() 119 | local attributes = {} 120 | -- try to fild 121 | while true do 122 | if prev == nil then 123 | break 124 | end 125 | if 126 | prev:type() == "line_comment" or prev:type() == "block_comment" 127 | then 128 | -- skip this 129 | elseif prev:type() == "attribute_item" then 130 | attributes[#attributes + 1] = 131 | vim.treesitter.get_node_text(prev, source) 132 | else 133 | break 134 | end 135 | prev = prev:prev_sibling() 136 | end 137 | ret.env_override["ATTRIBUTES_ITEMS"] = attributes 138 | 139 | if Config.get("snippet.rust.rstest_support") == true then 140 | -- check if this mod contains `use rstest::rstest;` 141 | local use_list = {} 142 | for _, body in ipairs(mod_item:field("body")) do 143 | for child in body:iter_children() do 144 | if child:type() == "use_declaration" then 145 | local nodes = child:field("argument") 146 | for _, use_node in ipairs(nodes) do 147 | local node_type = use_node:type() 148 | if node_type == "scoped_use_list" then 149 | vim.list_extend( 150 | use_list, 151 | flat_scoped_use_list(source, use_node) 152 | ) 153 | elseif node_type == "scoped_identifier" then 154 | use_list[#use_list + 1] = 155 | vim.treesitter.get_node_text(use_node, source) 156 | end 157 | end 158 | end 159 | end 160 | end 161 | ret.env_override["USE_LIST"] = use_list 162 | end 163 | end 164 | 165 | vim.api.nvim_win_set_cursor(0, { row, col }) 166 | return ret 167 | end 168 | ) 169 | end 170 | 171 | return { 172 | ls.s( 173 | { 174 | trig = "tfn", 175 | wordTrig = true, 176 | name = "(tfn) Test function definition", 177 | resolveExpandParams = inject_expanding_environment, 178 | }, 179 | d(1, function(_, parent) 180 | local env = parent.env 181 | local in_test_cfg = false 182 | if env["ATTRIBUTES_ITEMS"] ~= nil then 183 | for _, v in ipairs(env["ATTRIBUTES_ITEMS"]) do 184 | if v == "#[cfg(test)]" then 185 | in_test_cfg = true 186 | break 187 | end 188 | end 189 | end 190 | 191 | if in_test_cfg and env.MOD_ITEM_NAME == "tests" then 192 | local test_fn_attrs = { 193 | t("#[test]"), 194 | t("#[tokio::test]"), 195 | } 196 | 197 | if Config.get("snippet.rust.rstest_support") == true then 198 | local use_list = env["USE_LIST"] or {} 199 | if vim.list_contains(use_list, "rstest::rstest") then 200 | test_fn_attrs[#test_fn_attrs + 1] = t("#[rstest]") 201 | elseif vim.list_contains(use_list, "rstest::*") then 202 | test_fn_attrs[#test_fn_attrs + 1] = t("#[rstest]") 203 | end 204 | end 205 | 206 | -- function item 207 | return sn( 208 | nil, 209 | fmta( 210 | [[ 211 | 212 | fn test_() { 213 | 214 | } 215 | ]], 216 | { 217 | modifier = f(function(args, _) 218 | if vim.tbl_get(args, 1, 1) == "#[tokio::test]" then 219 | return "async " 220 | else 221 | return "" 222 | end 223 | end, { 2 }), 224 | name = i(1, "new_fn", { desc = "function name" }), 225 | attr = c(2, test_fn_attrs, { desc = "function attributes" }), 226 | body = i(0), 227 | } 228 | ) 229 | ) 230 | else 231 | -- function item 232 | return sn( 233 | nil, 234 | fmta( 235 | [[ 236 | fn () { 237 | 238 | } 239 | ]], 240 | { 241 | modifier = c(1, { 242 | t(""), 243 | t("async "), 244 | }, { desc = "function modifier" }), 245 | visible = c(2, { 246 | t(""), 247 | t("pub "), 248 | t("pub(crate) "), 249 | t("pub(super) "), 250 | }, { desc = "visibility" }), 251 | name = i(3, "new_fn", { desc = "function name" }), 252 | args = i(4, "args", { desc = "function arguments" }), 253 | body = i(0), 254 | } 255 | ) 256 | ) 257 | end 258 | end, {}) 259 | ), 260 | } 261 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/snippets/typescript.lua: -------------------------------------------------------------------------------- 1 | local ls = require("luasnip") 2 | local f = ls.function_node 3 | local snippet = require("luasnip-snippets.nodes").construct_snippet 4 | local fmta = require("luasnip.extras.fmt").fmta 5 | local extras = require("luasnip.extras") 6 | local rep = extras.rep 7 | local i = require("luasnip-snippets.nodes").insert_node 8 | local tsp = require("luasnip.extras.treesitter_postfix") 9 | local Utils = require("luasnip-snippets.utils") 10 | 11 | return { 12 | snippet { 13 | "pvf", 14 | name = "(pvf) public abstract function", 15 | dscr = "Expands to public abstract function declaration", 16 | mode = "bw", 17 | nodes = fmta( 18 | [[ 19 | public abstract (): ; 20 | ]], 21 | { 22 | name = i(1, "name", { desc = "Function Name" }), 23 | args = i(2, "", { desc = "Function Arguments" }), 24 | retType = i(3, "void", { desc = "Return Type" }), 25 | } 26 | ), 27 | }, 28 | } 29 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/utils/comment.lua: -------------------------------------------------------------------------------- 1 | ---Code from Comment.nvim 2 | ---https://github.com/numToStr/Comment.nvim/blob/master/lua/Comment/ft.lua 3 | 4 | ---Common commentstring shared b/w multiple languages 5 | local M = { 6 | cxx_l = "//%s", 7 | cxx_b = "/*%s*/", 8 | dbl_hash = "##%s", 9 | dash = "--%s", 10 | dash_bracket = "--[[%s]]", 11 | handlebars = "{{!--%s--}}", 12 | hash = "#%s", 13 | hash_bracket = "#[[%s]]", 14 | haskell_b = "{-%s-}", 15 | fsharp_b = "(*%s*)", 16 | html = "", 17 | latex = "%%s", 18 | semicolon = ";%s", 19 | lisp_l = ";;%s", 20 | lisp_b = "#|%s|#", 21 | twig = "{#%s#}", 22 | vim = '"%s', 23 | lean_b = "/-%s-/", 24 | } 25 | 26 | ---Lang table that contains commentstring (linewise/blockwise) for multiple filetypes 27 | ---Structure = { filetype = { linewise, blockwise } } 28 | ---@type table 29 | local L = setmetatable({ 30 | arduino = { M.cxx_l, M.cxx_b }, 31 | applescript = { M.hash }, 32 | astro = { M.html }, 33 | autohotkey = { M.semicolon, M.cxx_b }, 34 | bash = { M.hash }, 35 | beancount = { M.semicolon }, 36 | bib = { M.latex }, 37 | c = { M.cxx_l, M.cxx_b }, 38 | cabal = { M.dash }, 39 | cmake = { M.hash, M.hash_bracket }, 40 | conf = { M.hash }, 41 | conkyrc = { M.dash, M.dash_bracket }, 42 | coq = { M.fsharp_b }, 43 | cpp = { M.cxx_l, M.cxx_b }, 44 | cs = { M.cxx_l, M.cxx_b }, 45 | css = { M.cxx_b, M.cxx_b }, 46 | cuda = { M.cxx_l, M.cxx_b }, 47 | dart = { M.cxx_l, M.cxx_b }, 48 | dhall = { M.dash, M.haskell_b }, 49 | dosbatch = { "REM%s" }, 50 | dot = { M.cxx_l, M.cxx_b }, 51 | dts = { M.cxx_l, M.cxx_b }, 52 | editorconfig = { M.hash }, 53 | eelixir = { M.html, M.html }, 54 | elixir = { M.hash }, 55 | elm = { M.dash, M.haskell_b }, 56 | elvish = { M.hash }, 57 | faust = { M.cxx_l, M.cxx_b }, 58 | fennel = { M.semicolon }, 59 | fish = { M.hash }, 60 | func = { M.lisp_l }, 61 | fsharp = { M.cxx_l, M.fsharp_b }, 62 | gdb = { M.hash }, 63 | gdscript = { M.hash }, 64 | gitignore = { M.hash }, 65 | gleam = { M.cxx_l }, 66 | glsl = { M.cxx_l, M.cxx_b }, 67 | gnuplot = { M.hash, M.hash_bracket }, 68 | go = { M.cxx_l, M.cxx_b }, 69 | gomod = { M.cxx_l }, 70 | graphql = { M.hash }, 71 | groovy = { M.cxx_l, M.cxx_b }, 72 | handlebars = { M.handlebars, M.handlebars }, 73 | haskell = { M.dash, M.haskell_b }, 74 | haxe = { M.cxx_l, M.cxx_b }, 75 | heex = { M.html, M.html }, 76 | html = { M.html, M.html }, 77 | htmldjango = { M.html, M.html }, 78 | idris = { M.dash, M.haskell_b }, 79 | idris2 = { M.dash, M.haskell_b }, 80 | ini = { M.hash }, 81 | java = { M.cxx_l, M.cxx_b }, 82 | javascript = { M.cxx_l, M.cxx_b }, 83 | javascriptreact = { M.cxx_l, M.cxx_b }, 84 | jsonc = { M.cxx_l }, 85 | jsonnet = { M.cxx_l, M.cxx_b }, 86 | julia = { M.hash, "#=%s=#" }, 87 | kotlin = { M.cxx_l, M.cxx_b }, 88 | lean = { M.dash, M.lean_b }, 89 | lean3 = { M.dash, M.lean_b }, 90 | lidris = { M.dash, M.haskell_b }, 91 | lilypond = { M.latex, "%{%s%}" }, 92 | lisp = { M.lisp_l, M.lisp_b }, 93 | lua = { M.dash, M.dash_bracket }, 94 | luau = { M.dash, M.dash_bracket }, 95 | markdown = { M.html, M.html }, 96 | make = { M.hash }, 97 | mbsyncrc = { M.dbl_hash }, 98 | mermaid = { "%%%s" }, 99 | meson = { M.hash }, 100 | nextflow = { M.cxx_l, M.cxx_b }, 101 | nim = { M.hash, "#[%s]#" }, 102 | nix = { M.hash, M.cxx_b }, 103 | nu = { M.hash }, 104 | ocaml = { M.fsharp_b, M.fsharp_b }, 105 | odin = { M.cxx_l, M.cxx_b }, 106 | plantuml = { "'%s", "/'%s'/" }, 107 | purescript = { M.dash, M.haskell_b }, 108 | python = { M.hash }, -- Python doesn't have block comments 109 | php = { M.cxx_l, M.cxx_b }, 110 | prisma = { M.cxx_l }, 111 | proto = { M.cxx_l, M.cxx_b }, 112 | quarto = { M.html, M.html }, 113 | r = { M.hash }, -- R doesn't have block comments 114 | racket = { M.lisp_l, M.lisp_b }, 115 | rasi = { M.cxx_l, M.cxx_b }, 116 | readline = { M.hash }, 117 | rego = { M.hash }, 118 | remind = { M.hash }, 119 | rescript = { M.cxx_l, M.cxx_b }, 120 | robot = { M.hash }, -- Robotframework doesn't have block comments 121 | ron = { M.cxx_l, M.cxx_b }, 122 | ruby = { M.hash }, 123 | rust = { M.cxx_l, M.cxx_b }, 124 | sbt = { M.cxx_l, M.cxx_b }, 125 | scala = { M.cxx_l, M.cxx_b }, 126 | scheme = { M.lisp_l, M.lisp_b }, 127 | sh = { M.hash }, 128 | solidity = { M.cxx_l, M.cxx_b }, 129 | supercollider = { M.cxx_l, M.cxx_b }, 130 | sql = { M.dash, M.cxx_b }, 131 | stata = { M.cxx_l, M.cxx_b }, 132 | svelte = { M.html, M.html }, 133 | swift = { M.cxx_l, M.cxx_b }, 134 | sxhkdrc = { M.hash }, 135 | tablegen = { M.cxx_l, M.cxx_b }, 136 | teal = { M.dash, M.dash_bracket }, 137 | terraform = { M.hash, M.cxx_b }, 138 | tex = { M.latex }, 139 | template = { M.dbl_hash }, 140 | tmux = { M.hash }, 141 | toml = { M.hash }, 142 | twig = { M.twig, M.twig }, 143 | typescript = { M.cxx_l, M.cxx_b }, 144 | typescriptreact = { M.cxx_l, M.cxx_b }, 145 | typst = { M.cxx_l, M.cxx_b }, 146 | v = { M.cxx_l, M.cxx_b }, 147 | verilog = { M.cxx_l }, 148 | vhdl = { M.dash }, 149 | vim = { M.vim }, 150 | vifm = { M.vim }, 151 | vue = { M.html, M.html }, 152 | xdefaults = { "!%s" }, 153 | xml = { M.html, M.html }, 154 | xonsh = { M.hash }, -- Xonsh doesn't have block comments 155 | yaml = { M.hash }, 156 | yuck = { M.lisp_l }, 157 | zig = { M.cxx_l }, -- Zig doesn't have block comments 158 | }, { 159 | -- Support for compound filetype i.e. 'ios.swift', 'ansible.yaml' etc. 160 | __index = function(this, k) 161 | local base, fallback = string.match(k, "^(.-)%.(.*)") 162 | if not (base or fallback) then 163 | return nil 164 | end 165 | return this[base] or this[fallback] 166 | end, 167 | }) 168 | 169 | local ft = {} 170 | 171 | ---Sets a commentstring(s) for a filetype/language 172 | ---@param lang string Filetype/Language of the buffer 173 | ---@param val string|string[] 174 | ---@return table self Returns itself 175 | ---@usage [[ 176 | ---local ft = require('Comment.ft') 177 | --- 178 | -----1. Using method signature 179 | ----- Set only line comment or both 180 | ----- You can also chain the set calls 181 | ---ft.set('yaml', '#%s').set('javascript', {'//%s', '/*%s*/'}) 182 | --- 183 | ----- 2. Metatable magic 184 | ---ft.javascript = {'//%s', '/*%s*/'} 185 | ---ft.yaml = '#%s' 186 | --- 187 | ----- 3. Multiple filetypes 188 | ---ft({'go', 'rust'}, {'//%s', '/*%s*/'}) 189 | ---ft({'toml', 'graphql'}, '#%s') 190 | ---@usage ]] 191 | function ft.set(lang, val) 192 | L[lang] = type(val) == "string" and { val } or val --[[ @as string[] ]] 193 | return ft 194 | end 195 | 196 | ---Get line/block/both commentstring(s) for a given filetype 197 | ---@param lang string Filetype/Language of the buffer 198 | ---@param ctype? integer See |comment.utils.ctype|. If given `nil`, it'll 199 | ---return a copy of { line, block } commentstring. 200 | ---@return nil|string|string[] #Returns stored commentstring 201 | ---@usage [[ 202 | ---local ft = require('Comment.ft') 203 | ---local U = require('Comment.utils') 204 | --- 205 | ----- 1. Primary filetype 206 | ---ft.get('rust', U.ctype.linewise) -- `//%s` 207 | ---ft.get('rust') -- `{ '//%s', '/*%s*/' }` 208 | --- 209 | ----- 2. Compound filetype 210 | ----- NOTE: This will return `yaml` commenstring(s), 211 | ----- as `ansible` commentstring is not found. 212 | ---ft.get('ansible.yaml', U.ctype.linewise) -- `#%s` 213 | ---ft.get('ansible.yaml') -- { '#%s' } 214 | ---@usage ]] 215 | function ft.get(lang, ctype) 216 | local tuple = L[lang] 217 | if not tuple then 218 | return nil 219 | end 220 | if not ctype then 221 | return vim.deepcopy(tuple) 222 | end 223 | return tuple[ctype] 224 | end 225 | 226 | ---@export ft 227 | return setmetatable(ft, { 228 | __newindex = function(this, k, v) 229 | this.set(k, v) 230 | end, 231 | __call = function(this, langs, spec) 232 | for _, lang in ipairs(langs) do 233 | this.set(lang, spec) 234 | end 235 | return this 236 | end, 237 | }) 238 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/utils/common_cond.lua: -------------------------------------------------------------------------------- 1 | local Cond = require("luasnip-snippets.utils.cond") 2 | local Utils = require("luasnip-snippets.utils") 3 | 4 | local function line_begin_cond(line_to_cursor, matched_trigger, _) 5 | if matched_trigger == nil or line_to_cursor == nil then 6 | return false 7 | end 8 | return line_to_cursor:sub(1, -(#matched_trigger + 1)):match("^%s*$") 9 | end 10 | 11 | local function line_begin_show_maker(trig) 12 | local function line_begin_show(line_to_cursor) 13 | if line_to_cursor == nil then 14 | return false 15 | end 16 | local _, col = unpack(vim.api.nvim_win_get_cursor(0)) 17 | local line = vim.api.nvim_get_current_line() 18 | local trigger = line:sub(1, col):match("%S+$") 19 | if trigger == nil then 20 | return false 21 | end 22 | if #trigger > #trig then 23 | return false 24 | end 25 | return trigger == trig:sub(1, #trigger) 26 | end 27 | return line_begin_show 28 | end 29 | 30 | ---@param trig string 31 | ---@return LSSnippets.ConditionObject 32 | local function at_line_begin(trig) 33 | return Cond.make_condition(line_begin_cond, line_begin_show_maker(trig)) 34 | end 35 | 36 | local function generate_all_lines_before_match_cond(pattern) 37 | if type(pattern) == "string" then 38 | pattern = { pattern } 39 | end 40 | 41 | local function condition() 42 | local row, _ = unpack(vim.api.nvim_win_get_cursor(0)) 43 | local lines = vim.api.nvim_buf_get_lines(0, 0, row - 1, false) 44 | for _, line in ipairs(lines) do 45 | local match = false 46 | for _, p in ipairs(pattern) do 47 | if line:match(p) then 48 | match = true 49 | break 50 | end 51 | end 52 | if not match then 53 | return false 54 | end 55 | end 56 | return true 57 | end 58 | return Cond.make_condition(condition, condition) 59 | end 60 | 61 | local function has_select_raw_fn(_, _, _) 62 | return Utils.get_buf_var(0, "LUASNIP_SELECT_RAW") ~= nil 63 | end 64 | local has_select_raw = Cond.make_condition(has_select_raw_fn, has_select_raw_fn) 65 | 66 | return { 67 | at_line_begin = at_line_begin, 68 | generate_all_lines_before_match_cond = generate_all_lines_before_match_cond, 69 | has_select_raw = has_select_raw, 70 | } 71 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/utils/cond.lua: -------------------------------------------------------------------------------- 1 | ---@class luasnip-snippets.utils.cond 2 | local M = {} 3 | 4 | ---@class LSSnippets.ConditionFuncObject 5 | ---@operator unm: LSSnippets.ConditionFuncObject 6 | ---@operator add(LSSnippets.ConditionFuncObject): LSSnippets.ConditionFuncObject 7 | ---@operator div(LSSnippets.ConditionFuncObject): LSSnippets.ConditionFuncObject 8 | ---@operator pow(LSSnippets.ConditionFuncObject): LSSnippets.ConditionFuncObject 9 | ---@operator call(): boolean 10 | local ConditionFuncObject = { 11 | -- not '-' 12 | __unm = function(o1) 13 | return M.make_condition_func(function(...) 14 | return not o1(...) 15 | end) 16 | end, 17 | -- and '+' 18 | __add = function(o1, o2) 19 | return M.make_condition_func(function(...) 20 | return o1(...) and o2(...) 21 | end) 22 | end, 23 | -- or '/' 24 | __div = function(o1, o2) 25 | return M.make_condition_func(function(...) 26 | return o1(...) or o2(...) 27 | end) 28 | end, 29 | -- xor '^' 30 | __pow = function(o1, o2) 31 | return M.make_condition_func(function(...) 32 | return o1(...) ~= o2(...) 33 | end) 34 | end, 35 | -- use table like a function by overloading __call 36 | __call = function(tab, line_to_cursor, matched_trigger, captures) 37 | return tab.func(line_to_cursor, matched_trigger, captures) 38 | end, 39 | } 40 | 41 | ---@class LSSnippets.ConditionObject 42 | ---@field condition LSSnippets.ConditionFuncObject 43 | ---@field show_condition LSSnippets.ConditionFuncObject 44 | local ConditionObject = { 45 | ---@param tbl LSSnippets.ConditionObject 46 | ---@return LSSnippets.ConditionObject 47 | __unm = function(tbl) 48 | return M.make_condition(-tbl.condition, -tbl.show_condition) 49 | end, 50 | ---@param o1 LSSnippets.ConditionObject 51 | ---@param o2 LSSnippets.ConditionObject 52 | ---@return LSSnippets.ConditionObject 53 | __add = function(o1, o2) 54 | return M.make_condition( 55 | o1.condition + o2.condition, 56 | o1.show_condition + o2.show_condition 57 | ) 58 | end, 59 | ---@param o1 LSSnippets.ConditionObject 60 | ---@param o2 LSSnippets.ConditionObject 61 | ---@return LSSnippets.ConditionObject 62 | __div = function(o1, o2) 63 | return M.make_condition( 64 | o1.condition / o2.condition, 65 | o1.show_condition / o2.show_condition 66 | ) 67 | end, 68 | ---@param o1 LSSnippets.ConditionObject 69 | ---@param o2 LSSnippets.ConditionObject 70 | ---@return LSSnippets.ConditionObject 71 | __pow = function(o1, o2) 72 | return M.make_condition( 73 | o1.condition ^ o2.condition, 74 | o1.show_condition ^ o2.show_condition 75 | ) 76 | end, 77 | } 78 | 79 | ---@param fn function 80 | ---@return LSSnippets.ConditionFuncObject 81 | function M.make_condition_func(fn) 82 | return setmetatable({ func = fn }, ConditionFuncObject) 83 | end 84 | 85 | ---@param condition function|LSSnippets.ConditionFuncObject 86 | ---@param show_condition function|LSSnippets.ConditionFuncObject 87 | ---@return LSSnippets.ConditionObject 88 | function M.make_condition(condition, show_condition) 89 | if type(condition) == "function" then 90 | condition = M.make_condition_func(condition) 91 | end 92 | if type(show_condition) == "function" then 93 | show_condition = M.make_condition_func(show_condition) 94 | end 95 | 96 | return setmetatable({ 97 | condition = condition, 98 | show_condition = show_condition, 99 | }, ConditionObject) 100 | end 101 | 102 | return M 103 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/utils/dummy_types.lua: -------------------------------------------------------------------------------- 1 | -- dummy 2 | ---@class LuaSnip.Snippet 3 | 4 | ---@class LuaSnip.Node 5 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/utils/init.lua: -------------------------------------------------------------------------------- 1 | ---@class luasnip-snippets.utils 2 | local M = {} 3 | 4 | ---Replace all occurences of %s in template with match. 5 | ---@param match string|string[] 6 | ---@param template string 7 | ---@return string[] 8 | function M.replace_all(match, template) 9 | match = vim.F.if_nil(match, "") 10 | ---@type string 11 | local match_str = "" 12 | if type(match) == "table" then 13 | match_str = table.concat(match, "\n") 14 | else 15 | match_str = match 16 | end 17 | 18 | local ret = template:gsub("%%s", match_str) 19 | local ret_lines = vim.split(ret, "\n", { 20 | trimempty = false, 21 | }) 22 | 23 | return ret_lines 24 | end 25 | 26 | ---Load and concat snippets. 27 | ---@param base string 28 | ---@param snippets string[] 29 | ---@return LuaSnip.Snippet[] 30 | function M.concat_snippets(base, snippets) 31 | local ret = {} 32 | for _, snippet in ipairs(snippets) do 33 | local snippet_module = require(base .. "." .. snippet) 34 | if type(snippet_module) == "function" then 35 | snippet_module = snippet_module() 36 | end 37 | vim.list_extend(ret, snippet_module) 38 | end 39 | -- flatten the list 40 | local flat_ret = {} 41 | for _, snippet in ipairs(ret) do 42 | if vim.islist(snippet) then 43 | vim.list_extend(flat_ret, snippet) 44 | else 45 | flat_ret[#flat_ret + 1] = snippet 46 | end 47 | end 48 | return flat_ret 49 | end 50 | 51 | function M.reverse_list(lst) 52 | for i = 1, math.floor(#lst / 2) do 53 | local j = #lst - i + 1 54 | lst[i], lst[j] = lst[j], lst[i] 55 | end 56 | end 57 | 58 | function M.get_buf_var(bufnr, key) 59 | local succ, value = pcall(vim.api.nvim_buf_get_var, bufnr, key) 60 | if succ then 61 | return value 62 | end 63 | end 64 | 65 | return M 66 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/utils/tbl.lua: -------------------------------------------------------------------------------- 1 | ---@class luasnip-snippets.utils.tbl 2 | local M = {} 3 | 4 | function M.list_contains(t, value) 5 | for _, v in ipairs(t) do 6 | if v == value then 7 | return true 8 | end 9 | end 10 | return false 11 | end 12 | 13 | return M 14 | -------------------------------------------------------------------------------- /lua/luasnip-snippets/utils/treesitter.lua: -------------------------------------------------------------------------------- 1 | ---@class luasnip-snippets.utils.treesitter 2 | local M = {} 3 | 4 | ---Invoke the given function after the matched trigger removed and the buffer 5 | ---has been reparsed. 6 | ---@generic T 7 | ---@param ori_bufnr number 8 | ---@param match string 9 | ---@param fun fun(parser: vim.treesitter.LanguageTree, source: string):T 10 | ---@return T 11 | function M.invoke_after_reparse_buffer(ori_bufnr, match, fun) 12 | local function reparse_buffer() 13 | local row, col = unpack(vim.api.nvim_win_get_cursor(0)) 14 | local lines = vim.api.nvim_buf_get_lines(ori_bufnr, 0, -1, false) 15 | local current_line = lines[row] 16 | local current_line_left = current_line:sub(1, col - #match) 17 | local current_line_right = current_line:sub(col + 1) 18 | lines[row] = current_line_left .. current_line_right 19 | local lang = vim.treesitter.language.get_lang(vim.bo[ori_bufnr].filetype) 20 | or vim.bo[ori_bufnr].filetype 21 | 22 | local source = table.concat(lines, "\n") 23 | ---@type vim.treesitter.LanguageTree 24 | local parser = vim.treesitter.get_string_parser(source, lang) 25 | parser:parse(true) 26 | 27 | return parser, source 28 | end 29 | 30 | local parser, source = reparse_buffer() 31 | 32 | local ret = { fun(parser, source) } 33 | 34 | parser:destroy() 35 | 36 | return unpack(ret) 37 | end 38 | 39 | ---@param types table | string 40 | ---@return table 41 | function M.make_type_matcher(types) 42 | if type(types) == "string" then 43 | return { [types] = 1 } 44 | end 45 | 46 | if type(types) == "table" then 47 | if (vim.islist or vim.tbl_islist)(types) then 48 | local new_types = {} 49 | for _, v in ipairs(types) do 50 | new_types[v] = 1 51 | end 52 | return new_types 53 | end 54 | end 55 | 56 | return types 57 | end 58 | 59 | ---Find the first parent node whose type in `types`. 60 | ---@param node TSNode? 61 | ---@param types table|string 62 | ---@return TSNode|nil 63 | function M.find_first_parent(node, types) 64 | local matcher = M.make_type_matcher(types) 65 | 66 | ---@param root TSNode|nil 67 | ---@return TSNode|nil 68 | local function find_parent_impl(root) 69 | if root == nil then 70 | return nil 71 | end 72 | if matcher[root:type()] == 1 then 73 | return root 74 | end 75 | return find_parent_impl(root:parent()) 76 | end 77 | 78 | return find_parent_impl(node) 79 | end 80 | 81 | ---Returns the start pos of a `TSNode` 82 | ---@param node TSNode? 83 | ---@return { [1]: number, [2]: number }? 84 | function M.start_pos(node) 85 | if node == nil then 86 | return nil 87 | end 88 | local start_row, start_col, _, _ = vim.treesitter.get_node_range(node) 89 | return { start_row, start_col } 90 | end 91 | 92 | --- Normalize the arguments passed to treesitter_postfix into a function that 93 | --- returns treesitter-matches to the specified query+captures. 94 | ---@param opts LuaSnip.extra.MatchTSNodeOpts 95 | ---@return LuaSnip.extra.MatchTSNodeFunc 96 | function M.generate_match_tsnode_func(opts) 97 | local ts = require("luasnip.extras._treesitter") 98 | local match_opts = {} 99 | 100 | if opts.query then 101 | match_opts.query = vim.treesitter.query.parse(opts.query_lang, opts.query) 102 | else 103 | match_opts.query = 104 | vim.treesitter.query.get(opts.query_lang, opts.query_name or "luasnip") 105 | end 106 | 107 | match_opts.generator = ts.captures_iter(opts.match_captures or "prefix") 108 | 109 | if type(opts.select) == "function" then 110 | match_opts.selector = opts.select 111 | elseif type(opts.select) == "string" then 112 | match_opts.selector = ts.builtin_tsnode_selectors[opts.select] 113 | assert(match_opts.selector, "Selector " .. opts.select .. "is not known") 114 | else 115 | match_opts.selector = ts.builtin_tsnode_selectors.any 116 | end 117 | 118 | ---@param parser LuaSnip.extra.TSParser 119 | ---@param pos { [1]: number, [2]: number } 120 | return function(parser, pos) 121 | return parser:match_at( 122 | match_opts, --[[@as LuaSnip.extra.MatchTSNodeOpts]] 123 | pos 124 | ) 125 | end 126 | end 127 | 128 | ---@class LSSnippets.ProcessMatchesContext 129 | ---@field ts_parser LuaSnip.extra.TSParser 130 | ---@field best_match LuaSnip.extra.NamedTSMatch 131 | ---@field prefix_node TSNode 132 | ---@field matched_trigger string 133 | ---@field captures any 134 | ---@field pos { [1]: number, [2]: number } 135 | 136 | ---@alias LSSnippets.ProcessMatchesFunc fun(context: LSSnippets.ProcessMatchesContext, previous: table): table 137 | 138 | ---@param context LSSnippets.ProcessMatchesContext 139 | ---@param previous any 140 | function M.inject_tsmatches(context, previous) 141 | local start_row, start_col, _, _ = context.prefix_node:range() 142 | 143 | local env = { 144 | LS_TSMATCH = vim.split( 145 | context.ts_parser:get_node_text(context.prefix_node), 146 | "\n" 147 | ), 148 | -- filled subsequently. 149 | LS_TSDATA = {}, 150 | } 151 | for capture_name, node in pairs(context.best_match) do 152 | env["LS_TSCAPTURE_" .. capture_name:upper()] = 153 | vim.split(context.ts_parser:get_node_text(node), "\n") 154 | 155 | local from_r, from_c, to_r, to_c = node:range() 156 | env.LS_TSDATA[capture_name] = { 157 | type = node:type(), 158 | range = { { from_r, from_c }, { to_r, to_c } }, 159 | } 160 | end 161 | 162 | previous = vim.tbl_extend("force", previous, { 163 | trigger = context.matched_trigger, 164 | captures = context.captures, 165 | clear_region = { 166 | from = { 167 | start_row, 168 | start_col, 169 | }, 170 | to = { 171 | context.pos[1], 172 | context.pos[2] + #context.matched_trigger, 173 | }, 174 | }, 175 | env_override = env, 176 | }) 177 | 178 | return previous 179 | end 180 | 181 | ---@param match_tsnode LuaSnip.extra.MatchTSNodeFunc 182 | ---@param process_funcs LSSnippets.ProcessMatchesFunc[] 183 | function M.generate_resolve_expand_param(match_tsnode, process_funcs) 184 | ---@param snippet any 185 | ---@param line_to_cursor string 186 | ---@param matched_trigger string 187 | ---@param captures any 188 | ---@param parser vim.treesitter.LanguageTree 189 | ---@param source number|string 190 | ---@param bufnr number 191 | ---@param pos { [1]: number, [2]: number } 192 | return function( 193 | snippet, 194 | line_to_cursor, 195 | matched_trigger, 196 | captures, 197 | parser, 198 | source, 199 | bufnr, 200 | pos 201 | ) 202 | local ts = require("luasnip.extras._treesitter") 203 | 204 | local ts_parser = ts.TSParser.new(bufnr, parser, source) 205 | if ts_parser == nil then 206 | return 207 | end 208 | 209 | local row, col = unpack(pos) 210 | 211 | local best_match, prefix_node = match_tsnode(ts_parser, { row, col }) 212 | 213 | if best_match == nil or prefix_node == nil then 214 | return nil 215 | end 216 | 217 | ---@type LSSnippets.ProcessMatchesContext 218 | local context = { 219 | ts_parser = ts_parser, 220 | best_match = best_match, 221 | prefix_node = prefix_node, 222 | matched_trigger = matched_trigger, 223 | captures = captures, 224 | pos = pos, 225 | } 226 | local ret = {} 227 | for _, process_func in ipairs(process_funcs) do 228 | ret = process_func(context, ret) 229 | end 230 | 231 | return ret 232 | end 233 | end 234 | 235 | ---Optionally parse the buffer 236 | ---@param reparse boolean|string|nil 237 | ---@param real_resolver function 238 | ---@return fun(snippet, line_to_cursor, matched_trigger, captures):table? 239 | function M.wrap_with_reparse_context(reparse, real_resolver) 240 | local util = require("luasnip.util.util") 241 | local ts = require("luasnip.extras._treesitter") 242 | 243 | local function make_reparse_enter_and_leave_func( 244 | bufnr, 245 | trigger_region, 246 | trigger 247 | ) 248 | if reparse == "live" then 249 | local context = ts.FixBufferContext.new(bufnr, trigger_region, trigger) 250 | return function() 251 | return context:enter() 252 | end, function(_) 253 | context:leave() 254 | end 255 | elseif reparse == "copy" then 256 | local parser, source = 257 | ts.reparse_buffer_after_removing_match(bufnr, trigger_region) 258 | return function() 259 | return parser, source 260 | end, function() 261 | parser:destroy() 262 | end 263 | else 264 | return function() 265 | return vim.treesitter.get_parser(bufnr), bufnr 266 | end, function(_) end 267 | end 268 | end 269 | 270 | return function(snippet, line_to_cursor, matched_trigger, captures) 271 | local bufnr = vim.api.nvim_win_get_buf(0) 272 | local cursor = util.get_cursor_0ind() 273 | local trigger_region = { 274 | row = cursor[1], 275 | col_range = { 276 | -- includes from, excludes to. 277 | cursor[2] - #matched_trigger, 278 | cursor[2], 279 | }, 280 | } 281 | 282 | local enter, leave = 283 | make_reparse_enter_and_leave_func(bufnr, trigger_region, matched_trigger) 284 | local parser, source = enter() 285 | if parser == nil or source == nil then 286 | return nil 287 | end 288 | 289 | local ret = real_resolver( 290 | snippet, 291 | line_to_cursor, 292 | matched_trigger, 293 | captures, 294 | parser, 295 | source, 296 | bufnr, 297 | { cursor[1], cursor[2] - #matched_trigger } 298 | ) 299 | 300 | leave() 301 | 302 | return ret 303 | end 304 | end 305 | 306 | function M.treesitter_postfix(context, nodes, opts) 307 | local node_util = require("luasnip.nodes.util") 308 | local snip = require("luasnip.nodes.snippet").S 309 | 310 | opts = opts or {} 311 | vim.validate { 312 | context = { context, { "string", "table" } }, 313 | nodes = { nodes, "table" }, 314 | opts = { opts, "table" }, 315 | } 316 | 317 | context = node_util.wrap_context(context) 318 | context.wordTrig = false 319 | 320 | ---@type LuaSnip.extra.MatchTSNodeFunc 321 | local match_tsnode_func 322 | if type(context.matchTSNode) == "function" then 323 | match_tsnode_func = context.matchTSNode 324 | else 325 | match_tsnode_func = M.generate_match_tsnode_func(context.matchTSNode) 326 | end 327 | 328 | local expand_params_resolver = 329 | M.generate_resolve_expand_param(match_tsnode_func, { 330 | M.inject_tsmatches, 331 | context.injectMatches, 332 | }) 333 | 334 | context.resolveExpandParams = 335 | M.wrap_with_reparse_context(context.reparseBuffer, expand_params_resolver) 336 | 337 | return snip(context, nodes, opts) 338 | end 339 | 340 | return M 341 | --------------------------------------------------------------------------------